作者主頁:paper jie_博客
本文作者:大家好,我是paper jie,感謝你閱讀本文,歡迎一建三連哦。
本文于《JavaEE》專欄,本專欄是針對(duì)于大學(xué)生,編程小白精心打造的。筆者用重金(時(shí)間和精力)打造,將基礎(chǔ)知識(shí)一網(wǎng)打盡,希望可以幫到讀者們哦。
其他專欄:《MySQL》《C語言》《javaSE》《數(shù)據(jù)結(jié)構(gòu)》等
內(nèi)容分享:本期將會(huì)分享線程安全的集合類芝士
目錄
引入
多線程使用ArrayList
多線程使用隊(duì)列
多線程使用哈希表
Hashtable
ConcurrentHashMap
?相關(guān)面試題
引入
之前我們所學(xué)的集合類,大多數(shù)都是線程不安全的.,像ArrayList,LinkedList,Queue等都是線程不安全的.這里我們將介紹`線程安全的類
多線程使用ArrayList
1.可以自己使用synchronized來加鎖實(shí)現(xiàn)線程安全
2.Collections.synchronizedList(new ArrayList);
它就相當(dāng)于給ArrayList套了一個(gè)殼,通過這個(gè)殼得到了一個(gè)新的對(duì)象,這個(gè)新的對(duì)象的關(guān)鍵方法就加上了synchronized.
3.使用CopyOnWritArrayList
當(dāng)我們放容器中添加元素時(shí),不會(huì)往舊容器中添加,而是會(huì)將當(dāng)前這個(gè)容器的數(shù)據(jù)拷貝到一個(gè)新的容器中.添加完元素后,在將原有容器的引用指向新的容器.
它帶來的好處就是我們可以對(duì)CopyWritArrayList容器進(jìn)行并發(fā)的讀,不需要加鎖,因?yàn)楫?dāng)前容器不會(huì)添加任何元素.
優(yōu)點(diǎn)就是在讀多寫少的場(chǎng)景下,性能很高,不需要加鎖.
缺點(diǎn)就是比較占用內(nèi)存,新寫的數(shù)據(jù)不能第一時(shí)間讀到.且不適用與寫多的場(chǎng)景.
多線程使用隊(duì)列
1.ArrayListBlockingQueue
2.LinkedListBlockingQue
3.priorityBlockingQueue
4.TransferQueue
只包含一個(gè)元素的阻塞隊(duì)列.
多線程使用哈希表
Hashtable
在數(shù)據(jù)結(jié)構(gòu)中,我們學(xué)過hashmap和hashset,這兩者本身其實(shí)是線程不安全的.在所線程的環(huán)境下我們就可以使用Hashable和CouncurrentHashMap.
而我們的Hashtable就是在關(guān)鍵方法中加上了synchronized關(guān)鍵字,這其實(shí)就是直接對(duì)Hashtable對(duì)象本身直接加鎖.這就會(huì)出現(xiàn)一些問題:
如果多個(gè)線程訪問同一個(gè)Hashtable就會(huì)造成鎖沖突.
size屬性也是被synchronized控制,這樣鎖沖突會(huì)進(jìn)一步加大.
一但觸發(fā)擴(kuò)容,就會(huì)由該線程來完成擴(kuò)容過程,這個(gè)過程機(jī)會(huì)涉及到大量的元素拷貝,這里的速度就會(huì)很慢.
一個(gè)Hashtable就只有一個(gè)鎖,只要有兩個(gè)線程訪問這個(gè)HashTable中的任意一個(gè)數(shù)據(jù)就會(huì)發(fā)生鎖競(jìng)爭(zhēng).這里讀操作和其他鏈表的元素是不會(huì)發(fā)生線程安全問題的,但是這里還是會(huì)有鎖.
ConcurrentHashMap
相比于Hashtable,ConcurrentHashMap就做出了一系列的改進(jìn)和優(yōu)化.這里以Java1.8為例:
1. 讀操作沒有加鎖,只是使用了volatile保證從內(nèi)存讀取結(jié)果,只對(duì)寫操作進(jìn)行加鎖.加鎖的方式仍然是使用synchronized,只不過它加鎖的不是整個(gè)對(duì)象,而是"鎖桶",這就是每一個(gè)鏈表,這里用每個(gè)鏈表的頭節(jié)點(diǎn)來作為鎖對(duì)象,這樣就大大降低了鎖沖突的概率.
2. 充分的利用了CAS特性.size屬性就是使用CAS來更新的,這樣就又降低了鎖沖突的概率.
3. 優(yōu)化了擴(kuò)容方法,采用的是化整為零.
發(fā)現(xiàn)需要擴(kuò)容的線程,只需要?jiǎng)?chuàng)建出一個(gè)數(shù)組,再搬運(yùn)少量元素過去即可.
擴(kuò)容期間,新老數(shù)組同時(shí)存在
后面每個(gè)操作ConcurrentMap的線程都會(huì)參與搬運(yùn)數(shù)組的任務(wù),每個(gè)操作都會(huì)搬運(yùn)一小部分
直到搬運(yùn)完最后一個(gè)元素再將老數(shù)組刪除.
這個(gè)期間插入元素只往新數(shù)組中插入.
這個(gè)期間查找刪除元素新數(shù)組和老數(shù)組都需要查找和刪除.
?相關(guān)面試題
1. ConcurrentHashMap的讀是否要加鎖?
讀不需要加鎖,這樣可以減少鎖沖突.但是為了及時(shí)讀到剛修改的數(shù)據(jù),搭配了volatile關(guān)鍵字.
2. 介紹ConcurrentHashMap的分段技術(shù)?
這是Java1.7中采用的技術(shù).Java1.8中已經(jīng)不再使用了.它就是將若干個(gè)鏈表分成一個(gè)段,給段加上鎖,目的還是為了降低鎖競(jìng)爭(zhēng)的概率.當(dāng)兩個(gè)線程訪問的數(shù)據(jù)在一個(gè)段中就會(huì)發(fā)生鎖競(jìng)爭(zhēng).?
3.ConcurrentHashMap在jdk1.8做了哪些優(yōu)化?
取消了分段鎖,直接給每個(gè)哈希桶每個(gè)鏈表分配了一個(gè)鎖.將原來數(shù)組+鏈表的實(shí)現(xiàn)方式改變?yōu)閿?shù)組+鏈表+紅黑樹的方式.當(dāng)鏈表大于等于8時(shí)就會(huì)由鏈表轉(zhuǎn)變?yōu)榧t黑樹.?
4.Hashtable和HashMap,ConcurrentHashMap的區(qū)別?
HashMap: 線程不安全,key可以為null
Hashtable: 線程安全,使用synchronized鎖加在Hashtable對(duì)象上,效率低.且key不可以為null文章來源:http://www.zghlxwxcb.cn/news/detail-809412.html
ConcurrentHashMap: 線程安全,使用synchronized鎖加在每個(gè)鏈表上,鎖沖突率低,充分利用CAS機(jī)制,優(yōu)化了擴(kuò)容方式,key不能為null.文章來源地址http://www.zghlxwxcb.cn/news/detail-809412.html
到了這里,關(guān)于【JavaEE】線程安全的集合類的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!