import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gorilla.util.DateUtil;
import com.gorilla.util.cache.exception.LoadingCacheDataException;
public class EasyCache<K, V> implements Cache<K, V> {
// private Logger logger = Logger.getLogger(this.getClass());
private Logger logger = LoggerFactory.getLogger(this.getClass());
private ConcurrentHashMap<K, ValueHolder<V>> cacheMap = null;
private CacheLoader<K, V> loader;
// the default time(2 hours) which data to live for the duration.
private Long duration = 3600 * 1 * 1000L;
private Object loadingKey = new Object();
//private Object removeKey = new Object();
private CacheCleaner cacheCleaner;
private ScheduledThreadPoolExecutor scheduleExecutor = (ScheduledThreadPoolExecutor) Executors
.newScheduledThreadPool(1);
private final Long SCHEDULE_SLEEP_TIME_MIILLISECOND = 1 * 60 * 1000L;
EasyCache(CacheLoader<K, V> loader, Long duration) {
if (cacheMap == null)
cacheMap = new ConcurrentHashMap<K, ValueHolder<V>>();
if (duration != null && duration > 0)
this.duration = duration;
this.loader = loader;
scheduleExecutor.schedule(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
if (cacheMap.size() > 0) {
if (cacheCleaner == null) {
cacheCleaner = new ExpiredDurationCacheCleaner(duration);
}
Map<K, ValueHolder<V>> copyOfMap = new HashMap<K, ValueHolder<V>>(cacheMap);
Set<Entry<K, ValueHolder<V>>> copySets = copyOfMap.entrySet();
Iterator<Entry<K, ValueHolder<V>>> copyItors = copySets.iterator();
List<K> removeKeys = new ArrayList<K>();
Entry<K, ValueHolder<V>> entry;
while (copyItors.hasNext()) {
entry = copyItors.next();
if (cacheCleaner.cleanExpiredValue(entry.getValue().lastAccessTimeSecond))
removeKeys.add(entry.getKey());
}
if (removeKeys.size() > 0) {
synchronized (loadingKey){
for (K key : removeKeys) {
cacheMap.remove(key);
logger.info(String.format("Remove the Key:%s's data at %s", key,
DateUtil.getInstance().getYYYYMMDDWithTimeFormat().format(new Date())));
}
}
}
}
try {
Thread.sleep(SCHEDULE_SLEEP_TIME_MIILLISECOND);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
logger.error("thread sleep exception, e:" + e);
}
}
}
}, 1000, TimeUnit.MILLISECONDS);
}
@Override
public V get(K key) throws NullPointerException, LoadingCacheDataException {
// TODO Auto-generated method stub
if (cacheMap.get(key) == null) {
synchronized (loadingKey) {
if (cacheMap.get(key) == null) {
try {
V value = loader.load(key);
ValueHolder<V> holder = new ValueHolder<V>(value);
cacheMap.put(key, holder);
logger.info(String.format("The Key:%s's channelCategoryViews is loading to cache", key.toString()));
return value;
} catch (LoadingCacheDataException e) {
logger.error("get key:" + key.toString() + "'s data exception:" + e);
throw new LoadingCacheDataException(e.getMessage(), e);
} catch (NullPointerException e) {
logger.error("put data into cache Map exception", e);
throw new NullPointerException(e.getMessage());
}
} else {
ValueHolder<V> holder = cacheMap.get(key);
//holder.lastAccessTimeSecond = System.currentTimeMillis();
logger.debug(String.format("The Key:%s's new expired date is %s", key.toString(),
this.newExpiredDate(holder.lastAccessTimeSecond)));
return cacheMap.get(key).value;
}
}
} else {
ValueHolder<V> holder = cacheMap.get(key);
//holder.lastAccessTimeSecond = System.currentTimeMillis();
return holder.value;
}
}
@Override
public Boolean removeAll() {
// TODO Auto-generated method stub
Boolean result = true;
try {
if (cacheMap.size() > 0) {
synchronized (loadingKey){
cacheMap.clear();
}
}
} catch (Exception e) {
logger.error("clear cache error.", e);
result = false;
}
return result;
}
private Date newExpiredDate(Long lastAccessTimeSecond) {
Date date = new Date(this.duration + lastAccessTimeSecond);
DateUtil.getInstance().getYYYYMMDDWithTimeFormat().format(date);
return date;
}
private class ExpiredDurationCacheCleaner extends CacheCleaner {
public ExpiredDurationCacheCleaner(Long duration) {
super(duration);
// TODO Auto-generated constructor stub
}
/*
* param :time: the last access time result : return true if the data
* has expired and has to be remove from cache.
*/
@Override
public boolean cleanExpiredValue(long time) {
// TODO Auto-generated method stub
boolean result = false;
if ((System.currentTimeMillis() - time) > this.duration)
result = true; // clean the cache data.
else
result = false;
return result;
}
}
protected class ValueHolder<V> {
public Long lastAccessTimeSecond;
public V value;
public ValueHolder(V value) {
this.value = value;
this.lastAccessTimeSecond = System.currentTimeMillis();
}
}
public static void main(String[] args) {
Long newExpired = 60000L + System.currentTimeMillis();
Date d = new Date(newExpired);
Integer a = 100;
String test = String.format("The Key:%s's new expired date is %s", a.toString(),
DateUtil.getInstance().getYYYYMMDDWithTimeFormat().format(d));
out.println(test);
Map<String, Integer> aMap = new HashMap<String, Integer>();
}
}
2018年9月12日 星期三
EasyCache:輕量化、簡單使用的 記憶體暫存元件
EasyCache
訂閱:
張貼留言 (Atom)
沒有留言:
張貼留言