Java实现LRU缓存的实例详解

  

<强> Java实现LRU缓存的实例详解

  

<强>

1.缓存   

缓存对于代码系统的加速与优化具有极大的作用,对于码农来说是一个很熟悉的概念。可以说,你在内存中新增了一个一段空间(比方说数组、列表)存放一些冗余的结果数据,并利用这些数据完成了以空间换时间的优化目的,你就已经使用了缓存。
  

  

有服务级的缓存框架,如memcache、复述等。其实,很多时候,我们在自己同一个服务内,或者单个进程内也需要缓存,例如,lucene就对搜索做了缓存,而无须依赖外界。那么,我们如何实现我们自己的缓存?还要带自动失效的,最好还是LRU(最近最少使用)。

  

当你思考怎么去实现,你可能会想得很远。为了LRU,需要把刚使用的数据存入栈,或者纪录每个数据最近使用的时间,再来的定时扫描失效的线程…。其实,Java本身就已经为我们提供了LRU缓存很好的实现,即LinkedHashMap。

  

<强> 2. linkedhashmap分析

  

很多没有去细究过其内部实现的人,只是将其当作一个普通的hashMap来对待.LinkedHashMap是一个双向链表,加上HashTable的实现。表现出来与普通hashMap的一个区别就是LinkedHashMap会记录存入其中的数据的顺序,并能按顺取出。
  为了实现,一个哈希表,自然应该先申请在一片连续的内存空间上。当需要存入数据的时候,根据相应的散列值存入。而LinkedHashMap在这个基础上,为每个入口设置了之前与之后属性,形了一个双向链表,记录了他们把进入的前后顺序。

  

不仅如此,每当通过得到来获得某个元素后,得到方法内部,会在最后通过afterNodeAccess方法来调整链表的指向:

        空白afterNodeAccess (Nodee){//移动节点   LinkedHashMap.Entry最后一次;   如果(accessOrder,,(去年=尾)!=e) {   LinkedHashMap.Entryp=(LinkedHashMap.Entry) e、b=p。之前,一个=p.after;   p。后=零;   if (b==null)   头=;   其他的   b。在=;   如果(一个!=null)   一个。之前=b;   其他的   去年=b;   如果(去年==null)   头=p;   其他{   p。之前=去年;   最后一次。后=p;   }   尾=p;   + + modCount;   }   }   之前      

上述代码将e节点移至了双向链表的未尾。而在方法afterNodeInsertion中,只要满足条件,便移除最老的数据,即链表的头。

        空白afterNodeInsertion(布尔驱逐){//可能删除老大   LinkedHashMap.Entry第一个;   如果(驱逐,,(第一个=头)!=零,,removeEldestEntry(第一)){   K键=first.key;   removeNode(散列(关键),钥匙,null,假的,真的);   }   }   之前      

可见,当你为LinkedHashMap设置有限空间的时候,自然便完成了LRU缓存的效果。当然还有一个前提,你必须重写一个方法removeEldestEntry,返回真的。表示空间已满时,删除最老的。

        @Override   公共布尔removeEldestEntry (Map.Entry老大){   返回的大小()在能力;   }   之前      

<强> 3。线程安全的LRU缓存

  

如此,我们就获得了一个LRU缓存利器,满足了我们大多场景下的需求。但还有一个问题,它不是线程安全的。在多线程的情况下,你有可能需要对某些缓存做同步处理。这时候,你再找,可以看到java有ConcurrentHashMap的实现,但并不存在ConcurrentLinkedHashMap这样的类。
  

  

当然这个问题也不大,我们可以对再有的LinkedHashMap,再作封装,对get、put之类的方法加上同步操作。

  

目前,我们所用的处理,是直接采和谷歌提供的番石榴包,这里面就提供了我们想要的ConcurrentLinkedHashMap。这样就可以很方便地实现一个线程安全。具体代码如下:

        进口java.util.Set;      进口com.googlecode.concurrentlinkedhashmap.Weighers;   进口com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;   公共类ConcurrentLRUCache{   公共静态最终int DEFAULT_CONCURENCY_LEVEL=32;      私人最终ConcurrentLinkedHashMap地图;         公共ConcurrentLRUCache (int能力){   这(能力,DEFAULT_CONCURENCY_LEVEL);   }      公共ConcurrentLRUCache (int, int并发){   地图=new ConcurrentLinkedHashMap.Builder () .weigher(秤。你们;V>单例())   .initialCapacity(能力).maximumWeightedCapacity(能力)   .concurrencyLevel(并发).build ();   }      公共空间把(K键,V值){   地图。put(关键字,值);   }      公共V (K键){   V V=map.get(关键);   返回v;   }      公共V getInternal (K键){   返回map.get(关键);   }      公共空间删除(K键){   map.remove(关键);   }      公共长getCapacity () {   返回map.capacity ();   }         公共空间updateCapacity (int能力){   map.setCapacity(能力);   }         公共int getSize () {   返回map.size ();   }         公共空间clear () {   map.clear ();   }      公共Set

Java实现LRU缓存的实例详解