All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.support.cache.timeout.TimeoutCache Maven / Gradle / Ivy

package com.github.support.cache.timeout;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 
 * 超时内存缓存,当一个对象缓存了一定的时间,不管是否被访问过,都会被移除缓存,
 * 同时允许提供缓存对象数量限制,这个要看具体的业务需求,比如说本来整个业务中
 * 其实就只会缓存一个对象,就没必要限制数量了
 * 
* * @author Arvin * @time 2017/05/04 10:30 */ public class TimeoutCache { /** * 缓存项 * * @param KEY * @param Value */ class CacheItem implements Comparable> { CacheItem(K2 key, V2 object, long ttl) { this.key = key; this.cacheItem = object; this.ttl = ttl; this.createTime = System.currentTimeMillis(); } final K2 key; final V2 cacheItem; // 缓存的对象 long createTime; // 创建时间 long ttl; // objects timeout (time-to-live), 0 = no timeout AtomicLong accessCount = new AtomicLong(0); // 命中次数 boolean isExpired() { return ttl != 0 && createTime + ttl < System.currentTimeMillis(); } V2 getItem() { accessCount.incrementAndGet(); // 命中次数加一 return cacheItem; } @Override public int compareTo(CacheItem o) { return (int) (o.createTime - this.createTime); } } /** * 当缓存数量达到上限的时候,使用什么策略移除缓存项 */ enum PruneStrategy { /** 先进先出 */ FIFO, /** 按照命中率, 命中率低的将被移除 least frequently used */ LFU } /** * Timeout Cache Builder */ public static class Builder { /** 清空缓存的策略 */ private PruneStrategy pruneStrategy = PruneStrategy.LFU; /** 对象数量限制,等于0表示不限制数量 */ private int limitSize = 0; /** 默认超时时间,单位是毫秒, 如果是0表示永不超时,即一直会缓存,除非手动移除 */ private long defaultTimeout = 0; /** 每隔多长时间检测清空一次过期缓存, 单位是毫秒, 默认30秒, 如果timeout不为0,默认设置和timeout一致 */ private long pruneTimeoutInterval = 3000; public Builder pruneStrategy(PruneStrategy pruneStrategy) { this.pruneStrategy = pruneStrategy; return this; } public Builder limitSize(int limitSize) { this.limitSize = limitSize; return this; } public Builder defaultTimeout(long defaultTimeout) { this.defaultTimeout = defaultTimeout; return this; } public Builder pruneTimeoutInterval(long pruneTimeoutInterval) { this.pruneTimeoutInterval = pruneTimeoutInterval; return this; } public TimeoutCache build() { return new TimeoutCache(limitSize, defaultTimeout, pruneTimeoutInterval, pruneStrategy); } } /** * 构造器 */ public static Builder builder() { return new Builder(); } /** 清空缓存的策略 */ private final PruneStrategy pruneStrategy; /** 对象数量限制,等于0表示不限制数量 */ private final int limitSize; /** 默认超时时间,单位是毫秒, 如果是0表示永不超时,即一直会缓存,除非手动移除 */ private final long defaultTimeoutMilli; /** 每隔多长时间检测清空一次过期缓存, 单位是毫秒, 默认30秒, 如果timeout不为0,默认设置和timeout一致 */ private final long pruneTimeoutInterval; /** 用于缓存对象 */ private final Map> cacheMap = new HashMap>(); /** 记录每一个Key的读写锁,最大程度将锁的粒度缩小 */ private final ConcurrentHashMap lockMap = new ConcurrentHashMap(); /** 全局的读写锁 */ private final ReentrantReadWriteLock globalLock = new ReentrantReadWriteLock(); /** 定时器,用于定时清理无效对象 */ private Timer pruneTimer; public TimeoutCache() { this(0, 0, 30000, PruneStrategy.LFU); } public TimeoutCache(long defaultTimeoutMilli) { this(0, defaultTimeoutMilli, 30000, PruneStrategy.LFU); } /** * 构造函数 * * @param limitSize 缓存数量限制, 0表示不限制 * @param defaultTimeoutMilli 默认超时时间, 单位为毫秒, 0表示永不过期 * @param pruneTimeoutInterval 每隔多少毫秒进行一次将超时的缓存项移除,defaultTimeoutMilli=0的时候默认是30秒,否则按照用户输入的值 * @param pruneStrategy 永不过期的情况下,如果缓存数量限制,默认使用什么策略移除对象 */ public TimeoutCache(int limitSize, long defaultTimeoutMilli, long pruneTimeoutInterval, PruneStrategy pruneStrategy) { this.limitSize = limitSize < 1 ? 0 : limitSize; this.defaultTimeoutMilli = defaultTimeoutMilli < 1 ? 0 : defaultTimeoutMilli; this.pruneStrategy = null == pruneStrategy ? PruneStrategy.LFU : pruneStrategy; if (this.defaultTimeoutMilli == 0) { this.pruneTimeoutInterval = pruneTimeoutInterval < 1 ? 30000 : pruneTimeoutInterval; } else { // 如果输入了pruneTimeoutInterval且大于0就使用输入的,否则使用defaultTimeout this.pruneTimeoutInterval = pruneTimeoutInterval < 1 ? this.defaultTimeoutMilli : pruneTimeoutInterval; } schedulePrune(this.pruneTimeoutInterval); } public int getLimitSize() { return this.limitSize; } public int getCacheSize() { return this.cacheMap.values().size(); } public long getDefaultTimeoutMilli() { return this.defaultTimeoutMilli; } public long getPruneTimeoutInterval() { return pruneTimeoutInterval; } /** * Schedules prune. */ private void schedulePrune(long delay) { if (pruneTimer != null) { pruneTimer.cancel(); } pruneTimer = new Timer(); pruneTimer.schedule( new TimerTask() { @Override public void run() { pruneCache(false); } }, delay, delay); } /** * 判断缓存数量是否已经达到了限制 */ private boolean isFull() { return limitSize != 0 && limitSize <= getCacheSize(); } /** * 获取指定Key的lock对象,最细粒度进行并发控制 * * @param key key * @return 如果没有就创建一个 */ private ReentrantReadWriteLock getLock(K key) { ReentrantReadWriteLock lock = lockMap.get(key); if (lock != null) { return lock; } else { synchronized (lockMap) { // 双检 lock = lockMap.get(key); if (lock != null) { return lock; } lock = new ReentrantReadWriteLock(); lockMap.put(key, lock); return lock; } } } /** * 获取缓存中的对象 * * @param key 缓存的KEY */ public V get(K key) { ReentrantReadWriteLock.ReadLock globalReadLock = globalLock.readLock(); globalReadLock.lock(); // 具有全局读的锁才能进一步获取数据 ReentrantReadWriteLock lock = getLock(key); ReentrantReadWriteLock.ReadLock readLock = lock.readLock(); readLock.lock(); try { CacheItem cacheItem = cacheMap.get(key); if (cacheItem == null) { return null; } if (cacheItem.isExpired()) { // 如果过期了就返回null cacheMap.remove(key); return null; } return cacheItem.getItem(); } finally { globalReadLock.unlock(); readLock.unlock(); } } public void put(K key, V value) { put(key, value, defaultTimeoutMilli); } public void put(K key, V value, long timeout) { if (isFull()) { // 如果已经满了,需要按照一定策略移除缓存对象 pruneCache(true); } ReentrantReadWriteLock.ReadLock globalReadLock = globalLock.readLock(); globalReadLock.lock(); // 拥有全局的读锁才能进一步put ReentrantReadWriteLock lock = getLock(key); ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); writeLock.lock(); try { cacheMap.put(key, new CacheItem(key, value, timeout)); } finally { globalReadLock.unlock(); writeLock.unlock(); } } /** * 从缓存中删除执行的Key */ public void remove(K key) { ReentrantReadWriteLock lock = getLock(key); ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); writeLock.lock(); try { cacheMap.remove(key); } finally { writeLock.unlock(); } } /** * 使用指定策略移除缓存,如果defaultTimeout为0,使用过期和用户指定的策略进行移除 */ protected void pruneCache(boolean enabledStrategy) { ReentrantReadWriteLock.WriteLock writeLock = globalLock.writeLock(); writeLock.lock(); try { if (enabledStrategy && null != pruneStrategy) { switch (pruneStrategy) { case FIFO: pruneCacheByFIFO(); break; case LFU: pruneCacheByLFU(); break; } } else { pruneCacheByTimeout(null); } } finally { writeLock.unlock(); } } /** * 按照命中率移除命中率最低的一个 */ private int pruneCacheByLFU() { // 最少命中的缓存项 CacheItem leastAccessCacheItem = null; Collection> cacheItems = cacheMap.values(); for (CacheItem cacheItem : cacheItems) { if (null != cacheItem && (null == leastAccessCacheItem || leastAccessCacheItem.accessCount.get() > cacheItem.accessCount.get())) { leastAccessCacheItem = cacheItem; } } return pruneCacheByTimeout(leastAccessCacheItem); } /** * 按照先进先出规则移除, 当然过期的也会被移除 */ private int pruneCacheByFIFO() { // 先移除第一个进来的 CacheItem firstCacheItem = null; Collection> cacheItems = cacheMap.values(); for (CacheItem cacheItem : cacheItems) { if (null != cacheItem && (null == firstCacheItem || firstCacheItem.createTime > cacheItem.createTime)) { firstCacheItem = cacheItem; } } return pruneCacheByTimeout(firstCacheItem); } /** * 通过检测是否过期来移除缓存项 */ private int pruneCacheByTimeout(CacheItem readyRemoveCacheItem) { int removeCount = 0; Iterator> values = cacheMap.values().iterator(); while (values.hasNext()) { CacheItem cacheItem = values.next(); if (null != cacheItem && (cacheItem.equals(readyRemoveCacheItem) || cacheItem.isExpired())) { values.remove(); ++removeCount; } } return removeCount; } /** * 清空缓存 */ public void clear() { ReentrantReadWriteLock.WriteLock writeLock = globalLock.writeLock(); writeLock.lock(); try { this.cacheMap.clear(); } finally { writeLock.unlock(); } } /** * 清空匹配的缓存项 * * @param keyPattern cache key 的匹配规则 */ public void clear(String keyPattern) { ReentrantReadWriteLock.WriteLock writeLock = globalLock.writeLock(); writeLock.lock(); try { if (!this.cacheMap.isEmpty()) { List removeKeys = new ArrayList(); for (Map.Entry> entry : this.cacheMap.entrySet()) { if (entry.getKey() instanceof String) { if (entry.getKey().toString().matches(keyPattern)) { removeKeys.add(entry.getKey()); } } } if (!removeKeys.isEmpty()) { for (K key : removeKeys) { this.cacheMap.remove(key); } } } } finally { writeLock.unlock(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy