延迟阻塞队列DelayQueue
DelayQueue 是一个支持延时获取元素的阻塞队列,
内部采用优先队列 PriorityQueue 存储元素,
同时元素必须实现 Delayed 接口;在创建元素时可以指定多久才可以从队列中获取当前元素,只有在延迟期满时才能从队列中提取元素。
使用场景
- 缓存系统:当能够从延迟队列DelayQueue中获取到元素时,说明缓存已经过期
- 定时任务调度:一分钟后发送短信
基于延迟队列,实现一个缓存系统
延迟队列中添加的元素,实现了Delayed接口
public class CacheItem implements Delayed{ private long expireTime; private long currentTime; private String key; public String getKey() { return key; } public CacheItem(String key,long expireTime) { this.key = key; this.expireTime = expireTime; this.currentTime = System.currentTimeMillis(); } /** * 比较方法,用于排序 * 过期时间长的放队尾,时间短的放队首 */ @Override public int compareTo(Delayed o) { if(this.getDelay(TimeUnit.MICROSECONDS) > o.getDelay(TimeUnit.MICROSECONDS)) return 1; if(this.getDelay(TimeUnit.MICROSECONDS) > o.getDelay(TimeUnit.MICROSECONDS)) return -1; return 0; } /** * 计算剩余的过期时间 * 大于0说明没有过期 */ @Override public long getDelay(TimeUnit unit) { return expireTime - unit.MILLISECONDS.toSeconds(System.currentTimeMillis()-currentTime); } }
缓存实现
public class DelayQueueDemo { static class Cache implements Runnable{ private Map<String,String> itemMap = new HashMap<>(); private DelayQueue<CacheItem> delayQueue = new DelayQueue<>(); private boolean stop = false; // 初始化后就开始检测 public Cache() { new Thread(this).start(); } public void add(String key,String value,long expireTime) { CacheItem item = new CacheItem(key,expireTime); itemMap.put(key, value); delayQueue.add(item); } public String get(String key) { return itemMap.get(key); } public void shutdown() { stop = true; } // 开启多线程,检测缓存是否过期 @Override public void run() { while(!stop) { CacheItem item = delayQueue.poll(); if(item != null) { // 缓存过期 itemMap.remove(item.getKey()); System.out.println("delete expired key:"+item.getKey()); } } System.out.println("Cache stop"); } } public static void main(String[] args) throws Exception{ Cache cache = new Cache(); cache.add("a", "1", 1); cache.add("b", "2", 2); cache.add("c", "3", 2); cache.add("d", "4", 4); cache.add("e", "5", 6); while(true) { String a = cache.get("a"); String b = cache.get("b"); String c = cache.get("c"); String d = cache.get("d"); String e = cache.get("e"); if(a == null && b == null && c == null && d == null && e == null) { break; } } TimeUnit.SECONDS.sleep(1); cache.shutdown(); } }
延迟队列实现原理部分说明
- 可重入锁
ReentrantLock
- 优先队列
PriorityQueue