CopyOnWriteArrayList源码解析
此文已由作者趙計剛授權網易云社區發布。
歡迎訪問網易云社區,了解更多網易技術產品運營經驗。
注:在看這篇文章之前,如果對HashMap的層不清楚的話,建議先去看看HashMap源碼解析。
http://www.cnblogs.com/java-zhao/p/5106189.html
1、對于ConcurrentHashMap需要掌握以下幾點
Map的創建:ConcurrentHashMap()
往Map中添加鍵值對:即put(Object key, Object value)方法
獲取Map中的單個對象:即get(Object key)方法
刪除Map中的對象:即remove(Object key)方法
判斷對象是否存在于Map中:containsKey(Object key)
遍歷Map中的對象:即keySet().iterator(),在實際中更常用的是增強型的for循環去做遍歷
2、ConcurrentHashMap的創建
注:在往下看之前,心里先有這樣一個映像:ConcurrentHashMap的數據結構:一個指定個數的Segment數組,數組中的每一個元素Segment相當于一個HashTable。
2.1、使用方法:
Map<String,?Object>?map?=?new?ConcurrentHashMap<String,?Object>();2.2、源代碼:
?ConcurrentHashMap相關屬性:
????/***?用于分段*///?根據這個數來計算segment的個數,segment的個數是僅小于這個數且是2的幾次方的一個數(ssize)static?final?int?DEFAULT_CONCURRENCY_LEVEL?=?16;//?最大的分段(segment)數(2的16次方)static?final?int?MAX_SEGMENTS?=?1?<<?16;/***?用于HashEntry*///?默認的用于計算Segment數組中的每一個segment的HashEntry[]的容量,但是并不是每一個segment的HashEntry[]的容量static?final?int?DEFAULT_INITIAL_CAPACITY?=?16;//?默認的加載因子(用于resize)static?final?float?DEFAULT_LOAD_FACTOR?=?0.75f;//?用于計算Segment數組中的每一個segment的HashEntry[]的最大容量(2的30次方)static?final?int?MAXIMUM_CAPACITY?=?1?<<?30;/***?segments數組*?每一個segment元素都看做是一個HashTable*/final?Segment<K,?V>[]?segments;/***?用于擴容*/final?int?segmentMask;//?用于根據給定的key的hash值定位到一個Segmentfinal?int?segmentShift;//?用于根據給定的key的hash值定位到一個SegmentSegment類(ConcurrentHashMap的內部類)
????/***?一個特殊的HashTable*/static?final?class?Segment<K,?V>?extends?ReentrantLock?implementsSerializable?{private?static?final?long?serialVersionUID?=?2249069246763182397L;transient?volatile?int?count;//?該Segment中的包含的所有HashEntry中的key-value的個數transient?int?modCount;//?并發標記/**?元素個數超出了這個值就擴容?threshold==(int)(capacity?*?loadFactor)*?值得注意的是,只是當前的Segment擴容,所以這是Segment自己的一個變量,而不是ConcurrentHashMap的*/transient?int?threshold;transient?volatile?HashEntry<K,?V>[]?table;//?鏈表數組final?float?loadFactor;/***?這里要注意一個很不好的編程習慣,就是小寫l,容易與數字1混淆,所以最好不要用小寫l,可以改為大寫L*/Segment(int?initialCapacity,?float?lf)?{loadFactor?=?lf;//每個Segment的加載因子setTable(HashEntry.<K,?V>?newArray(initialCapacity));}/***?創建一個Segment數組,容量為i*/@SuppressWarnings("unchecked")static?final?<K,?V>?Segment<K,?V>[]?newArray(int?i)?{return?new?Segment[i];}/***?Sets?table?to?new?HashEntry?array.?Call?only?while?holding?lock?or?in*?constructor.*/void?setTable(HashEntry<K,?V>[]?newTable)?{threshold?=?(int)?(newTable.length?*?loadFactor);//?設置擴容值table?=?newTable;//?設置鏈表數組}說明:只列出了Segement的全部屬性和創建ConcurrentHashMap時所用到的方法。
HashEntry類(ConcurrentHashMap的內部類)
????/***?Segment中的HashEntry節點?類比HashMap中的Entry節點*/static?final?class?HashEntry<K,?V>?{final?K?key;//?鍵final?int?hash;//hash值volatile?V?value;//?實現線程可見性final?HashEntry<K,?V>?next;//?下一個HashEntryHashEntry(K?key,?int?hash,?HashEntry<K,?V>?next,?V?value)?{this.key?=?key;this.hash?=?hash;this.next?=?next;this.value?=?value;}/**?創建HashEntry數組,容量為傳入的i*/@SuppressWarnings("unchecked")static?final?<K,?V>?HashEntry<K,?V>[]?newArray(int?i)?{return?new?HashEntry[i];}}ConcurrentHashMap(int initialCapacity,float loadFactor,int concurrencyLevel)
?1?????/**2??????*?創建ConcurrentHashMap3??????*?@param?initialCapacity?用于計算Segment數組中的每一個segment的HashEntry[]的容量,?但是并不是每一個segment的HashEntry[]的容量4??????*?@param?loadFactor5??????*?@param?concurrencyLevel?用于計算Segment數組的大小(可以傳入不是2的幾次方的數,但是根據下邊的計算,最終segment數組的大小ssize將是2的幾次方的數)6??????*?7??????*?步驟:8??????*?這里以默認的無參構造器參數為例,initialCapacity==16,loadFactor==0.75f,concurrencyLevel==169??????*?1)檢查各參數是否符合要求 10??????*?2)根據concurrencyLevel(16),計算Segment[]的容量ssize(16)與擴容移位條件sshift(4) 11??????*?3)根據sshift與ssize計算將來用于定位到相應Segment的參數segmentShift與segmentMask 12??????*?4)根據ssize創建Segment[]數組,容量為ssize(16) 13??????*?5)根據initialCapacity(16)與ssize計算用于計算HashEntry[]容量的參數c(1) 14??????*?6)根據c計算HashEntry[]的容量cap(1) 15??????*?7)根據cap與loadFactor(0.75)為每一個Segment[i]都實例化一個Segment 16??????*?8)每一個Segment的實例化都做下面這些事兒: 17??????*?8.1)為當前的Segment初始化其loadFactor為傳入的loadFactor(0.75) 18??????*?8.2)創建一個HashEntry[],容量為傳入的cap(1) 19??????*?8.3)根據創建出來的HashEntry的容量(1)和初始化的loadFactor(0.75),計算擴容因子threshold(0) 20??????*?8.4)初始化Segment的table為剛剛創建出來的HashEntry 21??????*/ 22?????public?ConcurrentHashMap(int?initialCapacity,float?loadFactor,int?concurrencyLevel)?{ 23?????????//?檢查參數情況 24?????????if?(loadFactor?<=?0f?||?initialCapacity?<?0?||?concurrencyLevel?<=?0) 25?????????????throw?new?IllegalArgumentException(); 26? 27?????????if?(concurrencyLevel?>?MAX_SEGMENTS) 28?????????????concurrencyLevel?=?MAX_SEGMENTS; 29? 30?????????/** 31??????????*?找一個能夠正好小于concurrencyLevel的數(這個數必須是2的幾次方的數) 32??????????*?eg.concurrencyLevel==16==>sshift==4,ssize==16 33??????????*?當然,如果concurrencyLevel==15也是上邊這個結果 34??????????*/ 35?????????int?sshift?=?0; 36?????????int?ssize?=?1;//?segment數組的長度 37?????????while?(ssize?<?concurrencyLevel)?{ 38?????????????++sshift; 39?????????????ssize?<<=?1;//?ssize=ssize*2 40?????????} 41? 42?????????segmentShift?=?32?-?sshift;//?eg.segmentShift==32-4=28?用于根據給定的key的hash值定位到一個Segment 43?????????segmentMask?=?ssize?-?1;//?eg.segmentMask==16-1==15?用于根據給定的key的hash值定位到一個Segment 44?????????this.segments?=?Segment.newArray(ssize);//?構造出了Segment[ssize]數組?eg.Segment[16] 45? 46?????????/* 47??????????*?下面將為segment數組中添加Segment元素 48??????????*/ 49?????????if?(initialCapacity?>?MAXIMUM_CAPACITY) 50?????????????initialCapacity?=?MAXIMUM_CAPACITY; 51?????????int?c?=?initialCapacity?/?ssize;//?eg.initialCapacity==16,c==16/16==1 52?????????if?(c?*?ssize?<?initialCapacity)//?eg.initialCapacity==17,c==17/16=1,這時1*16<17,所以c=c+1==2 53?????????????++c;//?為了少執行這一句,最好將initialCapacity設置為2的幾次方 54?????????int?cap?=?1;//?每一個Segment中的HashEntry[]的初始化容量 55?????????while?(cap?<?c) 56?????????????cap?<<=?1;//?創建容量 57? 58?????????for?(int?i?=?0;?i?<?this.segments.length;?++i) 59?????????????//?這一塊this.segments.length就是ssize,為了不去計算這個值,可以直接改成i<ssize 60?????????????this.segments[i]?=?new?Segment<K,?V>(cap,?loadFactor); 61?????}注意:這個方法里邊我在頭部所寫的注釋非常重要,在這塊注釋寫明了:
每一個參數的作用
整個ConcurrentHashMap的一個創建步驟(以默認的參數值為例)
免費領取驗證碼、內容安全、短信發送、直播點播體驗包及云服務器等套餐
更多網易技術、產品、運營經驗分享請點擊。
相關文章:
【推薦】?十年?杭研技術秀 | “網易云存儲服務”從0到1發展之路
【推薦】?當我們在談論multidex65535時,我們在談論什么
轉載于:https://www.cnblogs.com/zyfd/p/10150816.html
總結
以上是生活随笔為你收集整理的CopyOnWriteArrayList源码解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vue.js 三种方式安装--npm安装
- 下一篇: UVa 489 Hangman Judg