并發(fā)容器的由來
在Java并發(fā)編程中断序,經(jīng)常聽到Java集合類流纹,同步容器、并發(fā)容器违诗,那么他們有哪些具體分類漱凝,以及各自之間的區(qū)別和優(yōu)劣呢?
只有把這些梳理清楚了诸迟,你才能真正掌握在高并發(fā)的環(huán)境下茸炒,正確使用好并發(fā)容器宇挫,我們先從Java集合類西傀,同步容器談起。
1.什么是同步容器
Java的集合容器框架中奈揍,主要有四大類別:List绅项、Set贮尖、Queue、Map趁怔,大家熟知的這些集合類ArrayList湿硝、LinkedList、HashMap這些容器都是非線程安全的润努。
如果有多個線程并發(fā)地訪問這些容器時关斜,就會出現(xiàn)問題。因此铺浇,在編寫程序時痢畜,在多線程環(huán)境下必須要求程序員手動地在任何訪問到這些容器的地方進(jìn)行同步處理,這樣導(dǎo)致在使用這些容器的時候非常地不方便鳍侣。
所以丁稀,Java先提供了同步容器供用戶使用。
同步容器可以簡單地理解為通過synchronized來實現(xiàn)同步的容器倚聚,比如Vector线衫、Hashtable以及SynchronizedList等容器。
2.同步容器惑折,主要的分類:
- Vector
- Stack
- HashTable
- Collections.synchronized方法生成
同步容器面臨的問題
可以通過查看Vector授账,Hashtable等這些同步容器的實現(xiàn)代碼,可以看到這些容器實現(xiàn)線程安全的方式就是將它們的狀態(tài)封裝起來惨驶,并在需要同步的方法上加上關(guān)鍵字synchronized白热。
這樣做的代價是削弱了并發(fā)性,當(dāng)多個線程共同競爭容器級的鎖時粗卜,吞吐量就會降低屋确。
例如: HashTable只要有一條線程獲取了容器的鎖之后,其他所有的線程訪問同步函數(shù)都會被阻塞续扔,因此同一時刻只能有一條線程訪問同步函數(shù)攻臀。
因此為了解決同步容器的性能問題,所以才有了并發(fā)容器测砂。
什么是并發(fā)容器
java.util.concurrent包中提供了多種并發(fā)類容器茵烈。
并發(fā)類容器是專門針對多線程并發(fā)設(shè)計的,使用了鎖分段技術(shù)砌些,只對操作的位置進(jìn)行同步操作呜投,但是其他沒有操作的位置其他線程仍然可以訪問,提高了程序的吞吐量存璃。
采用了CAS算法和部分代碼使用synchronized鎖保證線程安全仑荐。
并發(fā)容器有哪些分類
1.ConcurrentHashMap
對應(yīng)的非并發(fā)容器:HashMap
目標(biāo):代替Hashtable、synchronizedMap纵东,支持復(fù)合操作
原理:JDK6中采用一種更加細(xì)粒度的加鎖機(jī)制Segment“分段鎖”粘招,JDK8中采用CAS無鎖算法。
2.CopyOnWriteArrayList
對應(yīng)的非并發(fā)容器:ArrayList
目標(biāo):代替Vector偎球、synchronizedList
原理:利用高并發(fā)往往是讀多寫少的特性洒扎,對讀操作不加鎖辑甜,對寫操作,先復(fù)制一份新的集合袍冷,在新的集合上面修改磷醋,然后將新集合賦值給舊的引用,并通過volatile 保證其可見性胡诗,當(dāng)然寫操作的鎖是必不可少的了邓线。
3.CopyOnWriteArraySet
對應(yīng)的非并發(fā)容器:HashSet
目標(biāo):代替synchronizedSet
原理:基于CopyOnWriteArrayList實現(xiàn),其唯一的不同是在add時調(diào)用的是CopyOnWriteArrayList的addIfAbsent方法煌恢,其遍歷當(dāng)前Object數(shù)組骇陈,如Object數(shù)組中已有了當(dāng)前元素,則直接返回瑰抵,如果沒有則放入Object數(shù)組的尾部你雌,并返回。
4.ConcurrentSkipListMap
對應(yīng)的非并發(fā)容器:TreeMap
目標(biāo):代替synchronizedSortedMap(TreeMap)
原理:Skip list(跳表)是一種可以代替平衡樹的數(shù)據(jù)結(jié)構(gòu)谍憔,默認(rèn)是按照Key值升序的匪蝙。
5.ConcurrentSkipListSet
對應(yīng)的非并發(fā)容器:TreeSet
目標(biāo):代替synchronizedSortedSet
原理:內(nèi)部基于ConcurrentSkipListMap實現(xiàn)
6.ConcurrentLinkedQueue
不會阻塞的隊列
對應(yīng)的非并發(fā)容器:Queue
原理:基于鏈表實現(xiàn)的FIFO隊列(LinkedList的并發(fā)版本)
7.LinkedBlockingQueue、ArrayBlockingQueue习贫、PriorityBlockingQueue
對應(yīng)的非并發(fā)容器:BlockingQueue
特點:拓展了Queue逛球,增加了可阻塞的插入和獲取等操作
原理:通過ReentrantLock實現(xiàn)線程安全,通過Condition實現(xiàn)阻塞和喚醒
實現(xiàn)類:
- LinkedBlockingQueue:基于鏈表實現(xiàn)的可阻塞的FIFO隊列
- ArrayBlockingQueue:基于數(shù)組實現(xiàn)的可阻塞的FIFO隊列
- PriorityBlockingQueue:按優(yōu)先級排序的隊列
ConcurrentHashMap的實現(xiàn)
HashMap,Hashtable與ConcurrentHashMap都是實現(xiàn)的哈希表數(shù)據(jù)結(jié)構(gòu)苫昌,在隨機(jī)讀取的時候效率很高颤绕。
Hashtable實現(xiàn)同步是利用synchronized關(guān)鍵字進(jìn)行鎖定的,其是針對整張哈希表進(jìn)行鎖定的祟身,即每次鎖住整張表讓線程獨占奥务,在線程安全的背后是巨大的浪費。
ConcurrentHashMap和Hashtable主要區(qū)別就是圍繞著鎖的粒度進(jìn)行區(qū)別以及如何區(qū)鎖定袜硫。
上圖中氯葬,左邊是Hashtable的實現(xiàn)方式,可以看到鎖住整個哈希表婉陷;而右邊則是ConcurrentHashMap的實現(xiàn)方式帚称,單獨鎖住每一個桶(segment).ConcurrentHashMap將哈希表分為16個桶(默認(rèn)值),諸如get(),put(),remove()等常用操作只鎖當(dāng)前需要用到的桶,而size()才鎖定整張表秽澳。
原來只能一個線程進(jìn)入闯睹,現(xiàn)在卻能同時接受16個寫線程并發(fā)進(jìn)入(寫線程需要鎖定,而讀線程幾乎不受限制)担神。
所以楼吃,才有了并發(fā)性的極大提升。
高并發(fā)編程,除了并發(fā)容器孩锡,還會涉及到并發(fā)工具類:CountDownLatch等酷宵,后續(xù)將詳細(xì)的介紹并發(fā)工具類,以及ConcurrentHashMap的底層實現(xiàn)細(xì)節(jié)浮创,不僅要知其然,還要知其所以然忧吟,這樣才能更好的掌握好高并發(fā)編程。
本文由博客一文多發(fā)平臺 OpenWrite 發(fā)布斩披!