google的ConcurrentLinkedHashmap源代码解析

来源:互联网 发布:大数据公司如何盈利 编辑:程序博客网 时间:2024/05/20 05:24

转载自:http://janeky.iteye.com/blog/1534352


简述

ConcurrentLinkedHashMap 是google团队提供的一个容器。它有什么用呢?其实它本身是对

 

ConcurrentHashMap的封装,可以用来实现一个基于LRU策略的缓存。详细介绍可以参见 

 

http://code.google.com/p/concurrentlinkedhashmap

 

使用范例

Java代码  收藏代码
  1. public static void main(String[] args) {  
  2. ConcurrentLinkedHashMap<Integer, Integer> map = new   
  3. <span style="white-space: pre;">    </span>ConcurrentLinkedHashMap.Builder<Integer,Integer>().maximumWeightedCapacity(2).  
Java代码  收藏代码
  1. weigher(Weighers.singleton()).build();  
Java代码  收藏代码
  1. map.put(11);  
  2. map.put(22);  
  3. map.put(33);  
  4. System.out.println(map.get(1));//null 已经失效了  
  5. System.out.println(map.get(2));  

 

 ConcurrentLinkedHashMap 的构造函数比较特殊,它采用了Builder(构造器,GOF模式之一)。

 

它本身也是实现了ConcurrentMap接口的,所以使用起来跟ConcurrentHashMap一样。我们先put

 

进去三个元素,然后获取第一个元素,果然是null,因为基于LRU(最近使用)算法,key=1的节

 

点已经失效了。

 

源代码解析

先来看看它的整体框架


它本质是额外维护了一个双向链表,每次读和写都要改变相应节点的位置,将其移至队列头。

 

什么时候判断容易已经满了,是根据weight。每个元素都有一个weight,每增加一个元素,weight累计。当达到最大值的时候,就需要剔除最少操作的那个元素了,并且触发相关的事件。

 

我们先来看put函数

 

Java代码  收藏代码
  1. V put(K key, V value, boolean onlyIfAbsent) {  
  2.     checkNotNull(value);  
  3.   
  4.     final int weight = weigher.weightOf(key, value);//计算weight  
  5.     final WeightedValue<V> weightedValue = new WeightedValue<V>(value, weight);  
  6.     final Node node = new Node(key, weightedValue);//对数据进行包装,准备存入  
  7.   
  8. ConcurrentHashMap  
  9.   
  10.     for (;;) {  
  11.       final Node prior = data.putIfAbsent(node.key, node);  
  12.       if (prior == null) {//这个key之前没有值  
  13.         afterCompletion(new AddTask(node, weight));//更新后续操作  
  14.         return null;  
  15.       } else if (onlyIfAbsent) {  
  16.         afterCompletion(new ReadTask(prior));  
  17.         return prior.getValue();  
  18.       }  

 

AddTask 是判断是否容量满了,需要剔除其他元素

Java代码  收藏代码
  1. final class AddTask extends AbstractTask {  
  2.     final Node node;  
  3.     final int weight;  
  4.   
  5.     @Override  
  6.     @GuardedBy("evictionLock")  
  7.     public void run() {  
  8.       weightedSize += weight;  
  9.   
  10.       // ignore out-of-order write operations  
  11.       if (node.get().isAlive()) {  
  12.         evictionDeque.add(node);  
  13.         evict();//是否移除失效的  
  14.       }  
  15.     }  
  16.   
  17.   }  
  18.   
  19.  void evict() {  
  20.      
  21.     while (hasOverflowed()) {  
  22.       Node node = evictionDeque.poll();  
  23.   
  24.       // If weighted values are used, then the pending operations will adjust  
  25.       // the size to reflect the correct weight  
  26.       if (node == null) {  
  27.         return;  
  28.       }  
  29.   
  30.       // Notify the listener only if the entry was evicted  
  31.       if (data.remove(node.key, node)) {//移除失效的  
  32.         pendingNotifications.add(node);  
  33.       }  
  34.   
  35.       node.makeDead();  
  36.     }  
  37.   }  
 

get函数更简单一点,只是将这个key节点移至队列头

Java代码  收藏代码
  1. public V get(Object key) {  
  2.     final Node node = data.get(key);  
  3.     if (node == null) {  
  4.       return null;  
  5.     }  
  6.     afterCompletion(new ReadTask(node));  
  7.     return node.getValue();  
  8.   }  

 

性能比较 vs ConcurrentHashMap

不用说了,肯定是ConcurrentHashMap要好一点了,因为本文的主角还要维护一个操作队列嘛:)

不过性能上不是差很多,见下图。


总结:

利用ConcurrentLinkedHashMap来做基于LRU的缓存,还是值得推荐的。我们可以定义它的容器大小,基于LRU,就可以保证较高的命中率了。

 

参考资料:

http://code.google.com/p/concurrentlinkedhashmap

0 0
原创粉丝点击