java.util.concurrent.ConcurrentHashMap

一、简介

    ConcurrentHashMap是Map的一种并发实现,在该类中元素的read操作都是无锁了,而write操作需要被同步。这非常适合于读操作远大于写操作的情况。在实现过程中,ConcurrentHashMap将所有元素分成了若干个segment,每个segment是独立的,在一个segment上加锁并不影响其他segment的操作。segment本身是一个hashtable,对于一个加入ConcurrentHashMap的<key, value>对,key的hash值中的高位被用来索引segment,而低位用于segment中的索引。

二、segment实现

    segment是ConcurrentHashMap存储元素的基本段,它本身是一个hashtable的实现,read操作时无锁的,write需要同步,定义如下:

[java] view plain copy
  1. public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>     
  2.         implements ConcurrentMap<K, V>, Serializable {     
  3.     
  4.     
  5.     /**   
  6.      *  key, hash, next都是不可改的   
  7.    *  value值可被重写   
  8.    */    
  9.     static final class HashEntry<K,V> {     
  10.         final K key;     
  11.         final int hash;     
  12.         volatile V value;     
  13.         final HashEntry<K,V> next;     
  14.     
  15.         ...     
  16.     }     
  17.     
  18.     static final class Segment<K,V> extends ReentrantLock implements Serializable {     
  19.     
  20.         transient volatile int count;     
  21.         transient volatile HashEntry[] table;     
  22.         // 当segment中元素个数达到threshold时,需要rehash     
  23.         transient int threshold;     
  24.     }     
  25.     
  26.   ...     
  27. }     


segment的read操作:

[java] view plain copy
  1.  static final class Segment<K,V> extends ReentrantLock implements Serializable {     
  2.     
  3.       HashEntry<K,V> getFirst(int hash) {     
  4.           HashEntry[] tab = table;     
  5.           return (HashEntry<K,V>) tab[hash & (tab.length - 1)];     
  6.       }     
  7.          
  8.      V get(Object key, int hash) { // 该操作是无锁的     
  9.           if (count != 0) { // read-volatile     
  10.               HashEntry<K,V> e = getFirst(hash);     
  11.               while (e != null) {     
  12.                   if (e.hash == hash && key.equals(e.key)) {     
  13.                       V v = e.value;     
  14.                       if (v != null)     
  15.                           return v;     
  16.                       return readValueUnderLock(e); // recheck     
  17.                   }     
  18.                   e = e.next;     
  19.               }     
  20.           }     
  21.           return null;     
  22.       }     
  23.     
  24. ...     


由于HashEntry当中的key和next都是final的,所以segment之上的操作不可能影响HashEntry列表之间相对的顺序,而value是可变的,当第一次读值失败时,尝试加锁读。

    segment的replace操作:

[java] view plain copy
  1. static final class Segment<K,V> extends ReentrantLock implements Serializable {     
  2.     
  3.        /**   
  4.           * replace操作是就地替换,HashEntry的value是非final的   
  5.        */    
  6.         boolean replace(K key, int hash, V oldValue, V newValue) {     
  7.             lock();  // replace操作是同步的     
  8.         try {     
  9.                 // 得到该hash值对应的entry列表     
  10.            HashEntry<K,V> e = getFirst(hash);     
  11.                 while (e != null && (e.hash != hash || !key.equals(e.key)))     
  12.                     e = e.next;     
  13.     
  14.                 boolean replaced = false;     
  15.                 if (e != null && oldValue.equals(e.value)) { // 替换     
  16.               replaced = true;     
  17.                      e.value = newValue;     
  18.                 }     
  19.                 return replaced;     
  20.             } finally {     
  21.                 unlock();     
  22.             }     
  23.         }     
  24.     
  25.   ...     
  26. }    


segmet的put操作:

[java] view plain copy
  1. static final class Segment<K,V> extends ReentrantLock implements Serializable {     
  2.     
  3.         V put(K key, int hash, V value, boolean onlyIfAbsent) {     
  4.             lock(); // put是同步的     
  5.         try {     
  6.                 int c = count;     
  7.                 if (c++ > threshold) // ensure capacity     
  8.                     rehash();     
  9.                 HashEntry[] tab = table;     
  10.                 int index = hash & (tab.length - 1);     
  11.                 HashEntry<K,V> first = (HashEntry<K,V>) tab[index];     
  12.                 HashEntry<K,V> e = first;     
  13.                 while (e != null && (e.hash != hash || !key.equals(e.key)))     
  14.                     e = e.next;     
  15.     
  16.                 V oldValue;     
  17.                 if (e != null) { // 已存在则更新     
  18.              oldValue = e.value;     
  19.                     if (!onlyIfAbsent)     
  20.                         e.value = value;     
  21.                 }     
  22.                 else { // 新添加则加入列表头部     
  23.               oldValue = null;     
  24.                     ++modCount;     
  25.                     // HashEntry的next是只读的,新加入的entry只能放在头部     
  26.              tab[index] = new HashEntry<K,V>(key, hash, first, value);     
  27.                     count = c; // write-volatile     
  28.                 }     
  29.                 return oldValue;     
  30.             } finally {     
  31.                 unlock();     
  32.             }     
  33.         }     
  34.     
  35.   ...     
  36. }    


segment的remove操作一种copy on write 的方法,保留被删元素之后的列表,copy被删元素之前的hashEntry:

[java] view plain copy
  1. static final class Segment<K,V> extends ReentrantLock implements Serializable {     
  2.     
  3.         V remove(Object key, int hash, Object value) {     
  4.             lock();     
  5.             try {     
  6.                 int c = count - 1;     
  7.                 HashEntry[] tab = table;     
  8.                 int index = hash & (tab.length - 1);     
  9.                 HashEntry<K,V> first = (HashEntry<K,V>)tab[index];     
  10.                 HashEntry<K,V> e = first;     
  11.                 while (e != null && (e.hash != hash || !key.equals(e.key)))     
  12.                     e = e.next;     
  13.     
  14.                 V oldValue = null;     
  15.                 if (e != null) {     
  16.                     V v = e.value;     
  17.                     if (value == null || value.equals(v)) { // copy on write     
  18.                         oldValue = v;     
  19.                         ++modCount;     
  20.                         // e之后的列表可以保留,只需要重新创建e之前的HashEntry即可     
  21.                  HashEntry<K,V> newFirst = e.next;     
  22.                         // copy on write e之前的HashEntry     
  23.                         for (HashEntry<K,V> p = first; p != e; p = p.next)     
  24.                             newFirst = new HashEntry<K,V>(p.key, p.hash,       
  25.                                                           newFirst, p.value);     
  26.                         tab[index] = newFirst;     
  27.                         count = c; // write-volatile     
  28.                     }     
  29.                 }     
  30.                 return oldValue;     
  31.             } finally {     
  32.                 unlock();     
  33.             }     
  34.         }     
  35.     
  36.   ...     
  37. }    


  segment的rehash操作实现比较特别,为了保证rehash过程中copy的元素尽可能少,segment在rehash时Entry入口的个数是以2的倍数增长,这可以保证一个entry在rehash之后要么在原来的列表中,要么在下一个列表中:

[java] view plain copy
  1. static final class Segment<K,V> extends ReentrantLock implements Serializable {     
  2.     
  3.         void rehash() {     
  4.             // 局部变量引用table     
  5.             HashEntry[] oldTable = table;                 
  6.             int oldCapacity = oldTable.length;     
  7.             if (oldCapacity >= MAXIMUM_CAPACITY)     
  8.                 return;     
  9.     
  10.             // 右移1位相当于乘以2     
  11.             HashEntry[] newTable = new HashEntry[oldCapacity << 1];     
  12.             threshold = (int)(newTable.length * loadFactor);     
  13.             int sizeMask = newTable.length - 1;     
  14.             for (int i = 0; i < oldCapacity ; i++) {     
  15.                 // 第i个entry列表     
  16.            HashEntry<K,V> e = (HashEntry<K,V>)oldTable[i];     
  17.     
  18.                 if (e != null) {     
  19.                     HashEntry<K,V> next = e.next;     
  20.                     // 在新table上的索引     
  21.                     int idx = e.hash & sizeMask;     
  22.     
  23.                     if (next == null)     
  24.                         newTable[idx] = e;     
  25.                     else {     
  26.                         // 寻找该entry列表末端,rehash之后idx相同的元素     
  27.                         // 这些元素不需要被copy     
  28.                         HashEntry<K,V> lastRun = e;     
  29.                         int lastIdx = idx;     
  30.                         for (HashEntry<K,V> last = next;     
  31.                              last != null;     
  32.                              last = last.next) {     
  33.                             int k = last.hash & sizeMask;     
  34.                             if (k != lastIdx) {     
  35.                                 lastIdx = k;     
  36.                                 lastRun = last;     
  37.                             }     
  38.                         }     
  39.                         // 将lastRun之后的整个列表挂到新位置上     
  40.                         newTable[lastIdx] = lastRun;     
  41.     
  42.                         // Clone all remaining nodes     
  43.                         for (HashEntry<K,V> p = e; p != lastRun; p = p.next) {     
  44.                             int k = p.hash & sizeMask;     
  45.                             HashEntry<K,V> n = (HashEntry<K,V>)newTable[k];     
  46.                             newTable[k] = new HashEntry<K,V>(p.key, p.hash,     
  47.                                                              n, p.value);     
  48.                         }     
  49.                     }     
  50.                 }     
  51.             }     
  52.             table = newTable;     
  53.         }     
  54.     
  55.   ...     
  56. }    


 

三、ConcurrentHashMap方法实现

  ConcurrentHashMap在Segment的基础上,通过首先将<key, value>对hash到一个segment,再由segment实现对entry的管理。

    ConcurrentHashMap的get实现:

[java] view plain copy
  1. public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>     
  2.         implements ConcurrentMap<K, V>, Serializable {     
  3.     
  4.     final Segment<K,V> segmentFor(int hash) {     
  5.         return (Segment<K,V>) segments[(hash >>> segmentShift) & segmentMask];     
  6.     }     
  7.     
  8.     public V get(Object key) {     
  9.         int hash = hash(key); // throws NullPointerException if key null     
  10.         return segmentFor(hash).get(key, hash);     
  11.     }     
  12.     
  13.   ...     
  14. }    


ConcurrentHashMap的put和get方法:

[java] view plain copy
  1. public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>     
  2.         implements ConcurrentMap<K, V>, Serializable {     
  3.     
  4.     public V put(K key, V value) {     
  5.         if (value == null)     
  6.             throw new NullPointerException();     
  7.         int hash = hash(key);     
  8.         return segmentFor(hash).put(key, hash, value, false);     
  9.     }     
  10.     
  11.     public V remove(Object key) {     
  12.         int hash = hash(key);     
  13.         return segmentFor(hash).remove(key, hash, null);     
  14.     }     
  15.     
  16.   ...     
  17. }    


 

0
0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值