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)
沒有留言:
張貼留言