日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

JDK1.6的ConcurrentHashMap

發(fā)布時(shí)間:2024/3/12 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JDK1.6的ConcurrentHashMap 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1、構(gòu)造函數(shù):

? ? ? ?在構(gòu)造函數(shù)中,主要就是根據(jù)ConcurrentHashMap的loadfactor、initialCapacity和concurrencyLevel來初始化這個(gè)ConcurrentHashMap。下面分別來介紹這幾個(gè)參數(shù)的意義。

if (c++ > threshold) // ensure capacityrehash(); ? ? ? ?loadfactor是ConcurrentHashMap.Segment的負(fù)載因子。當(dāng)Segment中元素的數(shù)目達(dá)到了threshold,就會(huì)調(diào)用rehash來擴(kuò)容。Segment中的threshold的大小=Segment中表的大小*loadfactor。loadfactor的默認(rèn)大小是0.75,代表當(dāng)元素?cái)?shù)目達(dá)到表的3/4的時(shí)候就會(huì)對(duì)這個(gè)Segment進(jìn)行擴(kuò)容。
if (concurrencyLevel > MAX_SEGMENTS)concurrencyLevel = MAX_SEGMENTS;// Find power-of-two sizes best matching argumentsint sshift = 0;int ssize = 1;while (ssize < concurrencyLevel) {++sshift;ssize <<= 1;}segmentShift = 32 - sshift;segmentMask = ssize - 1;this.segments = Segment.newArray(ssize);

? ? ? ?concurrencyLevel是代表這個(gè)ConcurrentHashMap支持并發(fā)的等級(jí),也就是有多少個(gè)Segment。concurrencyLevel的大小總是2的指數(shù)次方,并且不會(huì)超過1<<16。

if (initialCapacity > MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY;int c = initialCapacity / ssize;if (c * ssize < initialCapacity)++c;int cap = 1;while (cap < c)cap <<= 1;for (int i = 0; i < this.segments.length; ++i)this.segments[i] = new Segment<K,V>(cap, loadFactor);

? ? ? ?initialCapacity是代表當(dāng)前ConcurrentHashMap中的初始大小,并不是每個(gè)Segment的大小,initialCapacity不超過1<<30。注意代碼中的cap,Segment的大小永遠(yuǎn)都是2的整數(shù)次冪大小,這樣能使元素更加平均的分布到segments中。

2、Segment

? ? ? ?Segment是一種分片鎖的思想,在介紹ConcurrentHashMap的其他方法之前要先知道Segment的一些屬性的作用。

transient volatile int count; //<span style="font-size:12px;">Segment中的元素?cái)?shù)目</span>。transient int modCount; //Segment中修改元素的次數(shù)。transient int threshold; //擴(kuò)容Segment的閾值。transient volatile HashEntry<K,V>[] table; //Segment儲(chǔ)存元素的地方。final float loadFactor; //負(fù)載因子。

? ? ? ?modCount調(diào)用的地方有put、remove、clear。replace則不會(huì)調(diào)用modCount,因?yàn)樗鼪]有Segment的表的物理結(jié)構(gòu)。

final Segment<K,V> segmentFor(int hash) {return segments[(hash >>> segmentShift) & segmentMask];} 根據(jù)hash的高位來將該鍵值對(duì)放在不同的Segment。

3、put

? ? ? ?put方法有兩個(gè)版本,put和putIfAbsent。這兩個(gè)方法的區(qū)別就是當(dāng)key在ConcurrentHashMap已經(jīng)存在的時(shí)候是否替換value。

V put(K key, int hash, V value, boolean onlyIfAbsent) {lock();try {int c = count;if (c++ > threshold) // ensure capacityrehash();HashEntry<K,V>[] tab = table;int index = hash & (tab.length - 1);HashEntry<K,V> first = 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;tab[index] = new HashEntry<K,V>(key, hash, first, value);count = c; // write-volatile}return oldValue;} finally {unlock();}} ? ? ? ?在對(duì)表進(jìn)行修改之前,會(huì)先加鎖。然后判斷是否需要對(duì)表擴(kuò)容。接下來根據(jù)hash值計(jì)算地址取鏈表頭。遍歷鏈表,尋找Key是否已經(jīng)在表里存在了,如果沒有存在則直接從鏈表頭插入,如果已經(jīng)存在則要根據(jù)onlyIfAbsent來判斷是否需要替換oldValue。返回oldValue,解鎖。

4、rehash

? ? ? ?rehash這個(gè)的訪問權(quán)限是default的,所以只有子類和同包的類才可以調(diào)用它。在ConcurrentHashMap中發(fā)現(xiàn)只有Segment的put方法才調(diào)用了rehash,所以在rehash中并不需要加鎖來防止出現(xiàn)并發(fā)問題。

void rehash() {HashEntry<K,V>[] oldTable = table;int oldCapacity = oldTable.length;if (oldCapacity >= MAXIMUM_CAPACITY)return;HashEntry<K,V>[] newTable = HashEntry.newArray(oldCapacity<<1);threshold = (int)(newTable.length * loadFactor);int sizeMask = newTable.length - 1;for (int i = 0; i < oldCapacity ; i++) {// We need to guarantee that any existing reads of old Map can// proceed. So we cannot yet null out each bin.HashEntry<K,V> e = oldTable[i];if (e != null) {HashEntry<K,V> next = e.next;int idx = e.hash & sizeMask;// Single node on listif (next == null)newTable[idx] = e;else {// Reuse trailing consecutive sequence at same slotHashEntry<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;}}newTable[lastIdx] = lastRun;// Clone all remaining nodesfor (HashEntry<K,V> p = e; p != lastRun; p = p.next) {int k = p.hash & sizeMask;HashEntry<K,V> n = newTable[k];newTable[k] = new HashEntry<K,V>(p.key, p.hash,n, p.value);}}}}table = newTable;} ? ? ? ?獲得Segment表的大小oldTable.length,然后乘2判斷是否超過1<<30。如果大于直接返回。否則會(huì)使用oldTable.length<<1創(chuàng)建新的數(shù)組,并且計(jì)算新的threshold。接下來遍歷oldTable把每個(gè)鏈表中的Entry重新插入到Segment中。因?yàn)镾egment的大小是2的整數(shù)次冪,所以原來位置鏈表中的Entry只有兩個(gè)選擇,一個(gè)是插入在原位curIdx的鏈表中,一個(gè)是插入到oldTable.length+curIdx的鏈表中。源碼中的處理方式,獲得i位置上的鏈表頭。如果非空,則判斷next是否為空,如果next非空直接插入到對(duì)應(yīng)位置做為鏈表頭。如果next不為空,則需要先遍歷一次鏈表,尋找最后一個(gè)idx不同的Entry。這里的優(yōu)化措施就是第一次遍歷鏈表記錄下lastRun,lastRun后面的Entry的idx都和lastRun的idx相同,所以不需要多次插入,只需把lastRun及l(fā)astRun后面的節(jié)點(diǎn)一次插入到新鏈表的頭后,然后開始重新遍歷鏈表,將剩余的表中Entry插入到鏈表頭中去。

5、get

? ? ? ?get也會(huì)交給相應(yīng)的Segment中去操作。但是get不會(huì)對(duì)表的結(jié)構(gòu)產(chǎn)生改變,所以不用加鎖。但是如果不加鎖的話會(huì)產(chǎn)生一致性的問題,put到Segment中的Entry,如果并發(fā)讀可能會(huì)訪問不到相應(yīng)的Entry。所以ConcurrentHashMap中的get是弱一致性的(具體方法大家可以參考http://ifeve.com/concurrenthashmap-weakly-consistent/)。

V get(Object key, int hash) {if (count != 0) { // read-volatileHashEntry<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;} 這里的count != 0的注釋和put方法中的count=c的注釋就解釋了這兩個(gè)操作是hb的,但是無法保證操作的原子性,所以沒辦法達(dá)到強(qiáng)一致性。

6、contain、containsValue

? ? ? ?contain就是調(diào)用了containValue,下面上containsValue的源碼。

public boolean containsValue(Object value) {if (value == null)throw new NullPointerException();// See explanation of modCount use abovefinal Segment<K,V>[] segments = this.segments;int[] mc = new int[segments.length];// Try a few times without lockingfor (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) {int sum = 0;int mcsum = 0;for (int i = 0; i < segments.length; ++i) {int c = segments[i].count;mcsum += mc[i] = segments[i].modCount;if (segments[i].containsValue(value))return true;}boolean cleanSweep = true;if (mcsum != 0) {for (int i = 0; i < segments.length; ++i) {int c = segments[i].count;if (mc[i] != segments[i].modCount) {cleanSweep = false;break;}}}if (cleanSweep)return false;}// Resort to locking all segmentsfor (int i = 0; i < segments.length; ++i)segments[i].lock();boolean found = false;try {for (int i = 0; i < segments.length; ++i) {if (segments[i].containsValue(value)) {found = true;break;}}} finally {for (int i = 0; i < segments.length; ++i)segments[i].unlock();}return found;}

? ? ? ?判斷ConcurrentHashMap中是否包含某個(gè)value就要在每個(gè)Segment去尋找是否包含這個(gè)value。首先先創(chuàng)建的mc數(shù)組就是記錄每個(gè)Segment的modCount,然后去執(zhí)行遍歷查找操作,查找成功則會(huì)返回true。如果不成功則會(huì)再次遍歷Segments的modCount去和mc中的每個(gè)值去比較,判斷是否某個(gè)Segment發(fā)生了改變。如果發(fā)生了改變,cleanSweep被設(shè)置為false,則會(huì)重新進(jìn)行。直到每個(gè)Segment在這期間都沒有被修改過,然后會(huì)返回false。int c = segments[i].modCount;保證了可見性。如果每次都發(fā)現(xiàn)segments的Entry被修改過,那么重試次數(shù)達(dá)到RETRIES_BEFORE_LOCK的時(shí)候就會(huì)繼續(xù)跳過無鎖化的查找,對(duì)每個(gè)Segment進(jìn)行加鎖,然后再進(jìn)行查找。

7、isEmpty

public boolean isEmpty() {final Segment<K,V>[] segments = this.segments;int[] mc = new int[segments.length];int mcsum = 0;for (int i = 0; i < segments.length; ++i) {if (segments[i].count != 0)return false;elsemcsum += mc[i] = segments[i].modCount;}if (mcsum != 0) {for (int i = 0; i < segments.length; ++i) {if (segments[i].count != 0 ||mc[i] != segments[i].modCount)return false;}}return true;}

? ? ? ?在isEmpty中還是使用了記錄segments的modCount的方式來進(jìn)行統(tǒng)計(jì),第一遍統(tǒng)計(jì),如果有某個(gè)Segment的count不為0則直接返回false,如果統(tǒng)計(jì)segments的count都為0,那么需要第二次遍歷segments,如果遍歷的時(shí)候某個(gè)Segment的count不為0則直接返回false。如果某個(gè)Segment的count為0,并且modCount不為0,說明發(fā)生了ABA問題,那么就返回false,因?yàn)樵趇sEmpty統(tǒng)計(jì)的過程中,segments并不為空。

轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/hahaha1232? ???

總結(jié)

以上是生活随笔為你收集整理的JDK1.6的ConcurrentHashMap的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。