問題:
在對集合迭代的時候,如果同時對其進行修改就會拋出java.util.ConcurrentModificationException異常,問題重現:
原因分析:
expectedModCount:表示對Map修改次數的期望值,它的初始值為modCount。
對圖一中的processes進行遍歷時,modCount == expectedModCount;
解決方案:
CopyOnWriteArrayList 介紹
(1)獨占鎖效率低:采用讀寫分離思想解決
既然獨占鎖的效率低下,那我們可以換一種方式,采用讀寫分離式的思想將讀操作和寫操作進行分開即可。
讀操作不加鎖,所有線程都不會阻塞。寫操作加鎖,線程會阻塞。
(2)寫線程獲取到鎖,其他線程包括讀線程阻塞
但是這時候又出現了另外一個問題了:寫線程獲取到鎖之后,其他的讀線程會陷入阻塞。
(3)復制思想:解決問題2
這咋辦呢?我們可以再轉化一下思想:
當我們往一個容器添加元素的時候,不直接往當前容器添加,而是先將當前容器進行 Copy,復制出一個新的容器,然后新的容器里添加元素,添加完元素之后,再將原容器的引用指向新的容器。
這時候會拋出來一個新的問題,也就是數據不一致的問題。如果寫線程還沒來得及寫會內存,其他的線程就會讀到了臟數據。
這就是CopyOnWriteArrayList 的思想和原理。就是拷貝一份寫。所以使用條件也很局限,那就是在讀多寫少的情況下比較好。
源碼分析(基於JDK1.8)
既然CopyOnWriteArrayList 主要是針對的讀寫操作,我們可以看看這兩個方法的源碼是如何實現的。
1、get方法
整個讀的過程沒有添加任何鎖,就是普通的數組獲取。
2、add方法
寫操作添加了一個鎖ReentrantLock,這個鎖我們可以決定在什么時候加鎖和釋放更加靈活。保證了寫操作線程安全。能夠體現Copy的思想代碼是 Arrays.copyOf(elements, len + 1);也就是數組復制了一份,最后setArray回去。
CopyOnWriteArrayList 總結
這個容器很簡單,雖然是采用了讀寫分離的思想,但是卻有很大不同,不同之處在於copy。
1、讀寫鎖
讀線程具有實時性,寫線程會阻塞。解決了數據不一致的問題。但是讀寫鎖依然會出現讀線程阻塞等待的情況
2、CopyOnWriteArrayList
讀線程具有實時性,寫線程會阻塞。不能解決數據不一致的問題。但是CopyOnWriteArrayList 不會出現讀線程阻塞等待的情況