Java併發程式設計之-list集合的併發.

我們都知道Java集合類中的arrayList是執行緒不安全的。那麼怎麼證明是執行緒不安全的呢?怎麼解決在併發環境下使用安全的list集合類呢?

本篇是《凱哥(凱哥Java:kagejava)併發程式設計學習》系列之《併發集合系列》教程的第一篇:

本文主要內容:怎麼證明arrayList不是執行緒安全的?怎麼解決這個問題?以及遇到問題解決的四個步驟及從原始碼來分析作者思路。

一:怎麼證明arrayList在併發情況下是執行緒不安全的呢?

建立一個list,用多個執行緒向list中新增資料。來看看結果

 

檢視執行結果:

 

我們發現了一個異常:java.util.ConcurrentModificationException

java.util.ConcurrentModificationException是什麼

這個異常什麼意思呢?我們來看看這個異常原始碼中類的註釋資訊:

 

This exception may be thrown by methods that have detected concurrent(此異常可能由檢測到併發的方法引發).

一般可以理解為,這是併發導致的異常。那麼在併發情況下出現了異常。是不是從側面說明arrayList是不安全的呢?

二:怎麼解決這個問題

這裡凱哥順便說下,解決問題的一般步驟。

1:怎麼操作導致的故障及現象是什麼?

操作:多個執行緒對list進行add新增操作的時候

結果:丟擲了java.util.ConcurrentModificationException異常資訊

2:分析產生這個問題的原因

舉個現實生活中的例子。簽到表,這個大家都見過吧,應該都簽到過吧。比如現在有個會議很多人來參與,需要簽到。現在,司小司正在簽到表上寫自己的名字時候,小明非要看簽到表上面有沒有自己名字。因為司小司正在簽到進行中,小明硬是要檢視,把簽到表搶過去,結果就是簽到表被撕壞了或者是司小司的筆在簽到表上留下了長長的痕跡。如果上面這個例子用計算機角度分析的話。

兩個執行緒(司小司和小明)對一個共享變數(簽到表,可以理解為是人名的集合)進行讀寫操作(司小司簽到是寫操作,小明要檢視自己是否簽到了,可以理解為讀操作),因為兩個執行緒都來競爭共享資源。後果就是簽到表被撕壞了或者是司小司的筆在簽到表上留下了長長的痕跡。異常現象。用到上面我們多個執行緒對list進行操作的時候,就拋異常了多執行緒併發修改異常資訊。

3:解決方案是什麼?

1:使用執行緒安全的List的子類Vectory

List list = new Vectory();

檢視vectory的add方法原始碼:

 

發現,原來vector的add方法是加的併發鎖來保證執行緒安全的

2:使用collections工具類的sync方法

List list = Colletcions.synchronizedList(new ArrayList<>());

檢視原始碼:

 

 

原來都是synchronized的。

我們在來看看synchronizedList方法上面的註釋。

 

發現,原來原始碼中是把整個list物件作為同步鎖的鎖。這樣來保證執行緒安全的

4:解決方案可以優化嗎?優化的建議是什麼?

我們知道synchronized關鍵字是同步鎖機制。強制並行轉化成序列的一種方案。這種對效能消耗比較大。有沒有更其他可以優化的方案嗎?

來看看使用JUC併發包下的:CopyOnWriteArrayList(寫時複製list)來解決吧。

先來看看這個類的add方法的原始碼:

 

從原始碼中,我們可以看到複製了一個新的list集合,將新元素在新集合中操作。那麼為什麼這種操作就不會出現併發異常呢?

因為這種思想,可以理解為讀寫分離的思想。因為get還是使用原來list的get的方法。寫的時候,在複製一份原來的,然後再複製出來的基礎上進行修改的。那麼怎麼保證資料問題呢?我們從原始碼中可以看到使用到了ReentrantLock(關於鎖相關的。凱哥(凱哥Java:kaigejava)將在後面詳細的講解的)鎖來控制的。

那麼現在使用CopyOnWriteArrayList來模擬下文章開頭簽到例子。

司小司再簽到的時候,先把簽到表複製一份,然後再新的複製出來的簽到表中進行簽到。小明是原來簽到表檢視自己的資訊的。這樣就不會出現爭強情況了。

&n