java.util.concurrent.ConcurrentHashMap

來源:互聯網
上載者:User

 

一、簡介

    ConcurrentHashMap是Map的一種並發實現,在該類中元素的read操作都是無鎖了,而write操作需要被同步。這非常適合於讀操作遠大於寫操作的情況。在實現過程中,ConcurrentHashMap將所有元素分成了若干個segment,每個segment是獨立的,在一個segment上加鎖並不影響其他segment的操作。segment本身是一個hashtable,對於一個加入ConcurrentHashMap的<key, value>對,key的hash值中的高位被用來索引segment,而低位用於segment中的索引。

二、segment實現

    segment是ConcurrentHashMap儲存元素的基本段,它本身是一個hashtable的實現,read操作時無鎖的,write需要同步,定義如下:

public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>           implements ConcurrentMap<K, V>, Serializable {           /**       *  key, hash, next都是不可改的     *  value值可被重寫     */      static final class HashEntry<K,V> {           final K key;           final int hash;           volatile V value;           final HashEntry<K,V> next;             ...       }         static final class Segment<K,V> extends ReentrantLock implements Serializable {             transient volatile int count;           transient volatile HashEntry[] table;           // 當segment中元素個數達到threshold時,需要rehash           transient int threshold;       }       ...   }   

segment的read操作:

 static final class Segment<K,V> extends ReentrantLock implements Serializable {           HashEntry<K,V> getFirst(int hash) {             HashEntry[] tab = table;             return (HashEntry<K,V>) tab[hash & (tab.length - 1)];         }               V get(Object key, int hash) { // 該操作是無鎖的             if (count != 0) { // read-volatile                 HashEntry<K,V> e = getFirst(hash);                 while (e != null) {                     if (e.hash == hash && key.equals(e.key)) {                         V v = e.value;                         if (v != null)                             return v;                         return readValueUnderLock(e); // recheck                     }                     e = e.next;                 }             }             return null;         }     ...   

由於HashEntry當中的key和next都是final的,所以segment之上的操作不可能影響HashEntry列表之間相對的順序,而value是可變的,當第一次讀值失敗時,嘗試加鎖讀。

    segment的replace操作:

static final class Segment<K,V> extends ReentrantLock implements Serializable {            /**            * replace操作是就地替換,HashEntry的value是非final的         */          boolean replace(K key, int hash, V oldValue, V newValue) {               lock();  // replace操作是同步的           try {                   // 得到該hash值對應的entry列表              HashEntry<K,V> e = getFirst(hash);                   while (e != null && (e.hash != hash || !key.equals(e.key)))                       e = e.next;                     boolean replaced = false;                   if (e != null && oldValue.equals(e.value)) { // 替換                 replaced = true;                        e.value = newValue;                   }                   return replaced;               } finally {                   unlock();               }           }       ...   }  

segmet的put操作:

static final class Segment<K,V> extends ReentrantLock implements Serializable {             V put(K key, int hash, V value, boolean onlyIfAbsent) {               lock(); // put是同步的           try {                   int c = count;                   if (c++ > threshold) // ensure capacity                       rehash();                   HashEntry[] tab = table;                   int index = hash & (tab.length - 1);                   HashEntry<K,V> first = (HashEntry<K,V>) tab[index];                   HashEntry<K,V> e = first;                   while (e != null && (e.hash != hash || !key.equals(e.key)))                       e = e.next;                     V oldValue;                   if (e != null) { // 已存在則更新                oldValue = e.value;                       if (!onlyIfAbsent)                           e.value = value;                   }                   else { // 新添加則加入列表頭部                 oldValue = null;                       ++modCount;                       // HashEntry的next是唯讀,新加入的entry只能放在頭部                tab[index] = new HashEntry<K,V>(key, hash, first, value);                       count = c; // write-volatile                   }                   return oldValue;               } finally {                   unlock();               }           }       ...   }  

segment的remove操作一種copy on write 的方法,保留被刪元素之後的列表,copy被刪元素之前的hashEntry:

static final class Segment<K,V> extends ReentrantLock implements Serializable {             V remove(Object key, int hash, Object value) {               lock();               try {                   int c = count - 1;                   HashEntry[] tab = table;                   int index = hash & (tab.length - 1);                   HashEntry<K,V> first = (HashEntry<K,V>)tab[index];                   HashEntry<K,V> e = first;                   while (e != null && (e.hash != hash || !key.equals(e.key)))                       e = e.next;                     V oldValue = null;                   if (e != null) {                       V v = e.value;                       if (value == null || value.equals(v)) { // copy on write                           oldValue = v;                           ++modCount;                           // e之後的列表可以保留,只需要重新建立e之前的HashEntry即可                    HashEntry<K,V> newFirst = e.next;                           // copy on write e之前的HashEntry                           for (HashEntry<K,V> p = first; p != e; p = p.next)                               newFirst = new HashEntry<K,V>(p.key, p.hash,                                                               newFirst, p.value);                           tab[index] = newFirst;                           count = c; // write-volatile                       }                   }                   return oldValue;               } finally {                   unlock();               }           }       ...   }  

  segment的rehash操作實現比較特別,為了保證rehash過程中copy的元素儘可能少,segment在rehash時Entry入口的個數是以2的倍數增長,這可以保證一個entry在rehash之後要麼在原來的列表中,要麼在下一個列表中:

static final class Segment<K,V> extends ReentrantLock implements Serializable {             void rehash() {               // 局部變數引用table               HashEntry[] oldTable = table;                           int oldCapacity = oldTable.length;               if (oldCapacity >= MAXIMUM_CAPACITY)                   return;                 // 右移1位相當於乘以2               HashEntry[] newTable = new HashEntry[oldCapacity << 1];               threshold = (int)(newTable.length * loadFactor);               int sizeMask = newTable.length - 1;               for (int i = 0; i < oldCapacity ; i++) {                   // 第i個entry列表              HashEntry<K,V> e = (HashEntry<K,V>)oldTable[i];                     if (e != null) {                       HashEntry<K,V> next = e.next;                       // 在新table上的索引                       int idx = e.hash & sizeMask;                         if (next == null)                           newTable[idx] = e;                       else {                           // 尋找該entry列表末端,rehash之後idx相同的元素                           // 這些元素不需要被copy                           HashEntry<K,V> lastRun = e;                           int lastIdx = idx;                           for (HashEntry<K,V> last = next;                                last != null;                                last = last.next) {                               int k = last.hash & sizeMask;                               if (k != lastIdx) {                                   lastIdx = k;                                   lastRun = last;                               }                           }                           // 將lastRun之後的整個列表掛到新位置上                           newTable[lastIdx] = lastRun;                             // Clone all remaining nodes                           for (HashEntry<K,V> p = e; p != lastRun; p = p.next) {                               int k = p.hash & sizeMask;                               HashEntry<K,V> n = (HashEntry<K,V>)newTable[k];                               newTable[k] = new HashEntry<K,V>(p.key, p.hash,                                                                n, p.value);                           }                       }                   }               }               table = newTable;           }       ...   }  

 

三、ConcurrentHashMap方法實現

  ConcurrentHashMap在Segment的基礎上,通過首先將<key, value>對hash到一個segment,再由segment實現對entry的管理。

    ConcurrentHashMap的get實現:

public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>           implements ConcurrentMap<K, V>, Serializable {         final Segment<K,V> segmentFor(int hash) {           return (Segment<K,V>) segments[(hash >>> segmentShift) & segmentMask];       }         public V get(Object key) {           int hash = hash(key); // throws NullPointerException if key null           return segmentFor(hash).get(key, hash);       }       ...   }  

ConcurrentHashMap的put和get方法:

public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>           implements ConcurrentMap<K, V>, Serializable {         public V put(K key, V value) {           if (value == null)               throw new NullPointerException();           int hash = hash(key);           return segmentFor(hash).put(key, hash, value, false);       }         public V remove(Object key) {           int hash = hash(key);           return segmentFor(hash).remove(key, hash, null);       }       ...   }  

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.