【数据结构和算法】FIFO, LFU, LRU缓存实现
一、LRU(Least Recently Used)-->最近最少使用的淘汰
(1)缓存接口
package com.sxf.study.interview.cache; /** * */ public interface Cache { String get(String key,CallBack callBack); } interface CallBack { String query(String key); }
(2)双向链表定义
package com.sxf.study.interview.cache; /** * */ public class LinkedNode { private String key; private String data; private LinkedNode preNode; private LinkedNode nextNode; public String getData() { return data; } public void setData(String data) { this.data = data; } public LinkedNode getPreNode() { return preNode; } public void setPreNode(LinkedNode preNode) { this.preNode = preNode; } public LinkedNode getNextNode() { return nextNode; } public void setNextNode(LinkedNode nextNode) { this.nextNode = nextNode; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } }
(3)并发查询相同key,防止雪崩查询
package com.sxf.study.interview.cache; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * 通过CountDownLatch对线程进行控制, * 一次只会有一个线程进入资源访问, * 其他线程在等待进入资源访问的线程结果 */ public class CacheValueFuture implements Future<String> { private CountDownLatch countDownLatch; private String key; private Object result; public CacheValueFuture(String key) { this.key = key; countDownLatch = new CountDownLatch(1); } @Override public boolean cancel(boolean mayInterruptIfRunning) { return false; } @Override public boolean isCancelled() { return false; } @Override public boolean isDone() { return result != null; } @Override public String get() throws InterruptedException, ExecutionException { countDownLatch.await(); if (result instanceof Throwable) { throw new ExecutionException((Throwable) result); } return (String) result; } @Override public String get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { countDownLatch.await(timeout, unit); if (result instanceof Throwable) { throw new ExecutionException((Throwable) result); } return (String) result; } public void setResult(Object result) { this.result = result; countDownLatch.countDown(); } }
(4)LRU cache定义
package com.sxf.study.interview.cache; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * LRU缓存 */ public class LRUCache implements Cache { private Object o=new Object(); /** * 防止缓存雪崩,对同一个key进行并发查询时,只会有1个线程进入查询 */ private Map<String, CacheValueFuture> cacheValueFutureHashMap = new ConcurrentHashMap<String, CacheValueFuture>(); /** * 维护缓存数据的热点 */ private LinkedNode headLinkedNode; /** * 维护缓存的非热点数据 */ private LinkedNode tailLinkedNode; /** * 快速查找缓存数据 */ private Map<String, LinkedNode> linkedNodeMap; /** * 当前缓存数据的个数 */ private int cacheSize; /** * 缓存数据的最大个数 */ private int maxCacheSize; public LRUCache(int maxCacheSize) { if (maxCacheSize <= 0) { throw new IllegalArgumentException("maxCacheSize <= 0"); } this.maxCacheSize = maxCacheSize; } @Override public String get(String key, CallBack callBack) { LinkedNode linkedNode = linkedNodeMap.get(key); String result; if (linkedNode == null) { //未命中缓存,降级去查询 CacheValueFuture cacheValueFuture = new CacheValueFuture(key); CacheValueFuture future=cacheValueFutureHashMap.putIfAbsent(key,cacheValueFuture); if(future!=null){ //已经存在其他线程,对该key进行查询了 try { result=future.get(); }catch (Exception e){ throw new RuntimeException("error"); } }else{ //第一个线程对该key进行查询,进行查询 try { result = callBack.query(key); //新加入节点 addNode(key,result); //设置结果 cacheValueFuture.setResult(result); }catch (Exception e){ cacheValueFuture.setResult(e); throw new RuntimeException("query error"); }finally { cacheValueFutureHashMap.remove(key); } } } else { //调整链表位置 adjustNode(linkedNode); result=linkedNode.getData(); } return result; } /** * 向链表中加入节点 * @param key * @param value */ private void addNode(String key,String value){ LinkedNode linkedNode=new LinkedNode(); linkedNode.setData(value); synchronized (o){ //链表节点数+1 cacheSize++; linkedNodeMap.put(key,linkedNode); //如果头节点为空,则代表链表为空 if(this.headLinkedNode==null){ this.headLinkedNode=linkedNode; this.tailLinkedNode=linkedNode; return; } //链表不为空,则新节点添加至链表的头节点 this.headLinkedNode.setPreNode(linkedNode); linkedNode.setNextNode(this.headLinkedNode); this.headLinkedNode=linkedNode; //链表的个数超限,删除尾巴节点 if(cacheSize>maxCacheSize) { LinkedNode temp=this.tailLinkedNode.getPreNode(); tailLinkedNode.setPreNode(null); temp.setNextNode(null); String tailKey=this.tailLinkedNode.getKey(); this.tailLinkedNode=temp; linkedNodeMap.remove(tailKey); } } } /** * 调整链表 * @param linkedNode */ private void adjustNode(LinkedNode linkedNode){ synchronized (o){ //当前节点为头节点,不用调整 if(linkedNode==this.headLinkedNode){ return ; } //当前节点为尾巴节点,需要进行调整 if(linkedNode==this.tailLinkedNode){ //断开尾巴节点,并设置新的尾巴节点 LinkedNode newTailNode=this.tailLinkedNode.getPreNode(); newTailNode.setNextNode(null); this.tailLinkedNode.setPreNode(null); LinkedNode newHeadNode=this.tailLinkedNode; this.tailLinkedNode=newTailNode; //设置新的头节点 this.headLinkedNode.setPreNode(newHeadNode); newHeadNode.setNextNode(this.headLinkedNode); this.headLinkedNode=newHeadNode; return; } //当前节点为中间节点,进行调整 //将当前节点从链表中摘除,上节点和下节点相连 LinkedNode preNode=linkedNode.getPreNode(); LinkedNode nextNode=linkedNode.getNextNode(); preNode.setNextNode(nextNode); nextNode.setPreNode(preNode); //将当前节点设置为头节点 this.headLinkedNode.setPreNode(linkedNode); linkedNode.setNextNode(this.headLinkedNode); this.headLinkedNode=linkedNode; } } }