private static ScheduledExecutorService swapExpiredPool = new ScheduledThreadPoolExecutor(10); private ReentrantLock lock = new ReentrantLock(); private ConcurrentHashMap<String, Node> cache = new ConcurrentHashMap<>(1024); /** * 讓過時時間最小的數據排在隊列前,在清除過時數據時 * ,只需查看緩存最近的過時數據,而不用掃描所有緩存 * * @see Node#compareTo(Node) * @see SwapExpiredNodeWork#run() */ private PriorityQueue<Node> expireQueue = new PriorityQueue<>(1024); public LocalCache() { //使用默認的線程池,每5秒清除一次過時數據 //線程池和調用頻率 最好是交給調用者去設置。 swapExpiredPool.scheduleWithFixedDelay( new SwapExpiredNodeWork(), 5, 5, TimeUnit.SECONDS); } public Object set(String key, Object value, long ttl) { Assert.isTrue(StringUtils.hasLength(key), "key can't be empty"); Assert.isTrue(ttl > 0, "ttl must greater than 0"); long expireTime = System.currentTimeMillis() + ttl; Node newNode = new Node(key, value, expireTime); lock.lock(); try { Node old = cache.put(key, newNode); expireQueue.add(newNode); //若是該key存在數據,還要從過時時間隊列刪除 if (old != null) { expireQueue.remove(old); return old.value; } return null; } finally { lock.unlock(); } } /** * 拿到的數據多是已通過期的數據,能夠再次判斷一下 * if(n.expireTime<System.currentTimeMillis()){ * return null; * } * 也能夠直接返回整個節點Node ,交給調用者去取捨 * <p> * <p> * 沒法判斷不存在該key,仍是該key存的是一個null值,若是須要區分這兩種狀況 * 能夠定義一個全局標識,標識key不存在 * public static final NOT_EXIST = new Object(); * 返回值時 * return n==null?NOT_EXIST:n.value; */ public Object get(String key) { Node n = cache.get(key); return n == null ? null : n.value; } /** * 刪出KEY,並返回該key對應的數據 */ public Object remove(String key) { lock.lock(); try { Node n = cache.remove(key); if (n == null) { return null; } else { expireQueue.remove(n); return n.value; } } finally { lock.unlock(); } } /** * 刪除已通過期的數據 */ private class SwapExpiredNodeWork implements Runnable { @Override public void run() { long now = System.currentTimeMillis(); while (true) { lock.lock(); try { Node node = expireQueue.peek(); //沒有數據了,或者數據都是沒有過時的了 if (node == null || node.expireTime > now) { return; } cache.remove(node.key); expireQueue.poll(); } finally { lock.unlock(); } } } } private static class Node implements Comparable<Node> { private String key; private Object value; private long expireTime; public Node(String key, Object value, long expireTime) { this.value = value; this.key = key; this.expireTime = expireTime; } /** * @see SwapExpiredNodeWork */ @Override public int compareTo(Node o) { long r = this.expireTime - o.expireTime; if (r > 0) { return 1; } if (r < 0) { return -1; } return 0; } }