摘要:一同步容器常用的一些容器例如都不是線程安全的,最簡(jiǎn)單的將這些容器變?yōu)榫€程安全的方式,是給這些容器所有的方法都加上關(guān)鍵字。為了降低哈希沖突的成本,在鏈表長(zhǎng)度超過(guò)時(shí),將鏈表轉(zhuǎn)換為紅黑樹(shù)。
一、同步容器
常用的一些容器例如 ArrayList、HashMap、都不是線程安全的,最簡(jiǎn)單的將這些容器變?yōu)榫€程安全的方式,是給這些容器所有的方法都加上 synchronized 關(guān)鍵字。
Java 的 Collections 中實(shí)現(xiàn)了這些同步容器:
簡(jiǎn)單的使用如下:
Listlist = Collections.synchronizedList(new ArrayList<>()); Map map = Collections.synchronizedMap(new HashMap<>()); Set set = Collections.synchronizedSet(new HashSet<>());
同步容器雖然簡(jiǎn)單,但是相應(yīng)的效率較低,因?yàn)殒i的粒度較大。
循環(huán)遍歷同步容器
如果在遍歷同步容器的時(shí)候,組合了多個(gè)方法,這會(huì)可能會(huì)存在競(jìng)態(tài)條件,仍然不是線程安全的。解決的辦法便是對(duì)容器加鎖。例如下面這樣:
public static void main(String[] args) { List二、并發(fā)容器list = Collections.synchronizedList(new ArrayList<>()); //省略添加數(shù)據(jù)的操作 String[] str = new String[list.size()]; int k = 0; synchronized (list){ Iterator iterator = list.iterator(); while (iterator.hasNext()){ str[k ++] = iterator.next(); } } }
Java 中還提供了一系列并發(fā)容器,相比于同步容器,其性能更好。并發(fā)容器共分為了四類:List、Map、Set、Queue。
1. ListList 中一個(gè)最主要的實(shí)現(xiàn)類是 CopyOnWriteArrayList ,CopyOnWrite,即寫(xiě)時(shí)復(fù)制,這樣的好處是讀操作是無(wú)鎖的。
其實(shí)現(xiàn)原理是內(nèi)部維護(hù)了一個(gè)數(shù)組,內(nèi)部變量 array 指向了這個(gè)數(shù)組。需要寫(xiě)時(shí),并不是在原數(shù)組上操作,而是將數(shù)組復(fù)制一份,在拷貝的數(shù)組中進(jìn)行寫(xiě)。完成后,將 array 指向新的數(shù)組。這樣一來(lái),讀寫(xiě)之間不互斥,效率得到了很大的提升。
需要注意的是 CopyOnWriteArrayList 適用于讀多寫(xiě)少的場(chǎng)景,并且需要接受讀寫(xiě)的暫時(shí)不一致,因?yàn)樵趯?xiě)的時(shí)候,并行的讀操作可能并不能馬上看到寫(xiě)的結(jié)果。
2. MapMap 的兩個(gè)主要實(shí)現(xiàn)類是 ConcurrentHashMap 和 ConcurrentSkipListMap,兩者主要的區(qū)別是:前者是無(wú)序的,后者是有序的。
在 Java 1.7 中,ConcurrentHashMap 的實(shí)現(xiàn)使用的是分段鎖技術(shù),其內(nèi)部主要的數(shù)據(jù)結(jié)構(gòu)是 Segment 和 HashEntry,ConcurrentHashMap 包含了一個(gè) Segment 數(shù)組,每個(gè) Segment 又包含一個(gè) HashEntry 數(shù)組,每個(gè) HashEntry 是一個(gè)存儲(chǔ)數(shù)據(jù)的鏈表結(jié)構(gòu)。
其中 Segment 繼承了 ReentrantLock,每個(gè) Segment 都有對(duì)應(yīng)的鎖,需要修改數(shù)據(jù)的時(shí)候,需要獲取這把鎖。修改不同的 Segment 數(shù)據(jù),則完全可以并行,效率得到了提升。示意圖如下:
Java 1.8 又對(duì) ConcurrentHashMap 做了較大的改進(jìn),放棄了分段鎖的技術(shù)。結(jié)構(gòu)和 Java 1.8 中的 HashMap 類似,采用的是數(shù)組+鏈表/紅黑樹(shù)來(lái)實(shí)現(xiàn)。為了降低哈希沖突的成本,在鏈表長(zhǎng)度超過(guò) 8 時(shí),將鏈表轉(zhuǎn)換為紅黑樹(shù)。使用 CAS 和 synchronized 解決并發(fā)問(wèn)題,鎖住鏈表或者紅黑樹(shù)的頭節(jié)點(diǎn),只要沒(méi)有哈希沖突,則不會(huì)出現(xiàn)并發(fā)問(wèn)題。示意圖如下:
ConcurrentSkipListMap 保證有序的主要原因是,底層使用的是跳表這種數(shù)據(jù)結(jié)構(gòu),關(guān)于跳表的介紹,你可以查看數(shù)據(jù)結(jié)構(gòu)中的內(nèi)容。
3. SetSet 的兩個(gè)實(shí)現(xiàn)是 CopyOnWriteArraySet 和 ConcurrentSkipListSet。
和前面說(shuō)到的 CopyOnWriteArrayList 、ConcurrentSkipListMap 實(shí)現(xiàn)的原理類似。
4. Queue隊(duì)列可以從兩方面進(jìn)行分類:
單端和雙端:?jiǎn)味岁?duì)列指的是只能在隊(duì)首出隊(duì),隊(duì)尾出隊(duì),而雙端隊(duì)列指的是隊(duì)首和隊(duì)尾均可入隊(duì)和出隊(duì)。
阻塞和非阻塞:阻塞隊(duì)列指的是,當(dāng)隊(duì)列滿的時(shí)候,入隊(duì)列阻塞;當(dāng)隊(duì)列空的時(shí)候,出隊(duì)列阻塞。
Java 中,單端隊(duì)列使用 queue 標(biāo)識(shí),雙端隊(duì)列使用 deque 標(biāo)識(shí)。
常用的實(shí)現(xiàn)類有:ArrayBlockingQueue、LinkedBlockingQueue、PriorityQueue。
其實(shí)現(xiàn)類是 ConcurrentLinkedQueue
其實(shí)現(xiàn)類是 LinkedBlockingDeque
其實(shí)現(xiàn)類是 ConcurrentLinkedDeque
使用其實(shí)都非常地簡(jiǎn)單,就是入隊(duì)出隊(duì)之類的操作,這里不再贅述了。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/74635.html
摘要:同樣,用類型的變量來(lái)保存這些值也不是線程安全的。僅保證可見(jiàn)性,無(wú)法保證線程安全性。并且返回的結(jié)果是對(duì)象,是局部變量,并未使對(duì)象逸出,所以這里也是線程安全的。 《Java并發(fā)編程實(shí)戰(zhàn)》第3章原文 《Java并發(fā)編程實(shí)戰(zhàn)》中3.4.2 示例:使用Volatile類型來(lái)發(fā)布不可變對(duì)象 在前面的UnsafeCachingFactorizer類中,我們嘗試用兩個(gè)AtomicReferences變...
摘要:同步容器及其注意事項(xiàng)中的容器主要可以分為四個(gè)大類,分別是和,但并不是所有的容器都是線程安全的。并發(fā)容器及其注意事項(xiàng)在版本之前所謂的線程安全的容器,主要指的就是同步容器,當(dāng)然因?yàn)樗蟹椒ǘ加脕?lái)保證互斥,串行度太高了,性能太差了。 Java 并發(fā)包有很大一部分內(nèi)容都是關(guān)于并發(fā)容器的,因此學(xué)習(xí)和搞懂這部分的內(nèi)容很有必要。 Java 1.5 之前提供的同步容器雖然也能保證線程安全,但是性能很差...
摘要:今天主要講解的是本文力求簡(jiǎn)單講清每個(gè)知識(shí)點(diǎn),希望大家看完能有所收獲一和回顧線程安全的和我們知道是用于替代的,是線程安全的容器。使用迭代器遍歷時(shí)不需要顯示加鎖,看看與方法的實(shí)現(xiàn)可能就有點(diǎn)眉目了。 前言 只有光頭才能變強(qiáng) showImg(https://segmentfault.com/img/remote/1460000016931828?w=1120&h=640); 前一陣子寫(xiě)過(guò)一篇C...
摘要:下面是線程相關(guān)的熱門(mén)面試題,你可以用它來(lái)好好準(zhǔn)備面試。線程安全問(wèn)題都是由全局變量及靜態(tài)變量引起的。持有自旋鎖的線程在之前應(yīng)該釋放自旋鎖以便其它線程可以獲得自旋鎖。 最近看到網(wǎng)上流傳著,各種面試經(jīng)驗(yàn)及面試題,往往都是一大堆技術(shù)題目貼上去,而沒(méi)有答案。 不管你是新程序員還是老手,你一定在面試中遇到過(guò)有關(guān)線程的問(wèn)題。Java語(yǔ)言一個(gè)重要的特點(diǎn)就是內(nèi)置了對(duì)并發(fā)的支持,讓Java大受企業(yè)和程序員...
摘要:它能阻塞一組線程直到某個(gè)事件發(fā)生。與閉鎖的區(qū)別所有線程必須同時(shí)到達(dá)柵欄位置,才能繼續(xù)執(zhí)行。閉鎖用于等待事件,而柵欄用于等待其它線程。閉鎖一旦進(jìn)入終止?fàn)顟B(tài),就不能被重置,它是一次性對(duì)象,而柵欄可以重置。 同步容器。它的原理是將狀態(tài)封裝起來(lái),并對(duì)每個(gè)公有方法都實(shí)行同步,使得每次只有1個(gè)線程能夠訪問(wèn)容器的狀態(tài)。 Vector和HashTable Collections.synchroni...
閱讀 1820·2021-08-13 15:06
閱讀 3108·2021-08-05 10:02
閱讀 3384·2019-08-30 15:55
閱讀 2396·2019-08-30 13:46
閱讀 2496·2019-08-30 13:01
閱讀 1334·2019-08-29 17:17
閱讀 2834·2019-08-29 15:27
閱讀 1441·2019-08-29 11:12