java實現帶過期時間的快取
阿新 • • 發佈:2019-02-15
直接上程式碼
package com.dyh.utils; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import java.util.PriorityQueue; import java.util.concurrent.*; import java.util.concurrent.locks.ReentrantLock; public class LocalCache { 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; } } }
有啥不足或者錯誤的地方歡迎留言哈。
另外,在PriorityQueue的時候,發現它的預設初始話容量是11,不知道有啥深意,知道的老哥指導下。。