CopyOnWriteArrayList詳解
CopyOnWriteArrayList是ArrayList 的一個執行緒安全的變體,其中所有可變操作(add、set等等)都是通過對底層陣列進行一次新的複製來實現的。
這一般需要很大的開銷,但是當遍歷操作的數量大大超過可變操作的數量時,這種方法可能比其他替代方法更 有效。在不能或不想進行同步遍歷,但又需要從併發執行緒中排除衝突時,它也很有用。“快照”風格的迭代器方法在建立迭代器時使用了對陣列狀態的引用。此陣列在迭代器的生存期內不會更改,因此不可能發生衝突,並且迭代器保證不會丟擲ConcurrentModificationException。建立迭代器以後,迭代器就不會反映列表的新增、移除或者更改。在迭代器上進行的元素更改操作(remove、set和add)不受支援。這些方法將丟擲UnsupportedOperationException。允許使用所有元素,包括null。
記憶體一致性效果:當存在其他併發 collection 時,將物件放入CopyOnWriteArrayList之前的執行緒中的操作
happen-before 隨後通過另一執行緒從CopyOnWriteArrayList中訪問或移除該元素的操作。
這種情況一般在多執行緒操作時,一個執行緒對list進行修改。一個執行緒對list進行fore時會出現java.util.ConcurrentModificationException錯誤。
下面來看一個列子:兩個執行緒一個執行緒fore一個執行緒修改list的值。
01 |
package
|
02 |
03 |
import
|
04 |
import
|
05 |
import
|
06 |
import
|
07 |
08 |
public CopyOnWriteArrayListDemo {
|
09 |
/**
|
10 |
* 讀執行緒
|
11 |
* @author wangjie
|
12 |
*
|
13 |
*/
|
14 |
private static class
implements
|
15 |
List<String> list;
|
16 |
17 |
public ReadTask(List<String> list) {
|
18 |
this .list = list;
|
19 |
}
|
20 |
21 |
public void run() {
|
22 |
for (String str : list) {
|
23 |
System.out.println(str);
|
24 |
}
|
25 |
}
|
26 |
}
|
27 |
/**
|
28 |
* 寫執行緒
|
29 |
* @author wangjie
|
30 |
*
|
31 |
*/
|
32 |
private static class
implements
|
33 |
List<String> list;
|
34 |
int index;
|
35 |
36 |
public WriteTask(List<String> list, int index) {
|
37 |
this .list = list;
|
38 |
this .index = index;
|
39 |
}
|
40 |
41 |
public void run() {
|
42 |
list.remove(index);
|
43 |
list.add(index, "write_" + index);
|
44 |
}
|
45 |
}
|
46 |
47 |
public void run() {
|
48 |
final int NUM = 10 ;
|
49 |
List<String> list = new ArrayList<String>();
|
50 |
for ( int
0 ; i < NUM; i++) {
|
51 |
list.add( "main_" + i);
|
52 |
}
|
53 |
ExecutorService executorService = Executors.newFixedThreadPool(NUM);
|
54 |
for ( int
0 ; i < NUM; i++) {
|
55 |
executorService.execute( new ReadTask(list));
|
56 |
executorService.execute( new WriteTask(list, i));
|
57 |
}
|
58 |
executorService.shutdown();
|
59 |
}
|
60 |
61 |
public static void
|
62 |
new CopyOnWriteArrayListDemo().run();
|
63 |
}
|
64 |
} |
從結果中可以看出來。在多執行緒情況下報錯。其原因就是多執行緒操作結果:那這個種方案不行我們就換個方案。用jdk自帶的類CopyOnWriteArrayList來做容器。這個類和ArrayList最大的區別就是add(E) 的時候。容器會自動copy一份出來然後再尾部add(E)。看原始碼:
01 |
/** |
02 |
* Appends the specified element to the end of this list.
|
03 |
*
|
04 |
* @param e element to be appended to this list
|
05 |
* @return <tt>true</tt> (as specified by {@link Collection#add})
|
06 |
*/
|
07 |
public boolean add(E e) {
|
08 |
final ReentrantLock lock = this .lock;
|
09 |
lock.lock();
|
10 |
try {
|
11 |
Object[] elements = getArray();
|
12 |
int len = elements.length;
|
13 |
Object[] newElements = Arrays.copyOf(elements, len + 1 );
|
14 |
newElements[len] = e;
|
15 |
setArray(newElements);
|
16 |
return true ;
|
17 |
} finally {
|
18 |
lock.unlock();
|
19 |
}
|
20 |
}
|
用到了Arrays.copyOf 方法。這樣導致每次操作的都不是同一個引用。也就不會出現java.util.ConcurrentModificationException錯誤。
換了種方案看程式碼:
1 |
// List<String> list = new ArrayList<String>(); |
2 |
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
|
也就把容器list換成了 CopyOnWriteArrayList,其他的沒變。執行緒裡面的list不用改。因為 CopyOnWriteArrayList實現的也是list<E> 介面。看結果:
其結果沒報錯。
CopyOnWriteArrayList add(E) 和remove(int index)都是對新的陣列進行修改和新增。所以在多執行緒操作時不會出現java.util.ConcurrentModificationException錯誤。
所以最後得出結論:CopyOnWriteArrayList適合使用在讀操作遠遠大於寫操作的場景裡,比如快取。發生修改時候做copy,新老版本分離,保證讀的高效能,適用於以讀為主的情況。
相關文章
- java中CopyOnWriteArrayList詳解Java
- 圖解集合3:CopyOnWriteArrayList圖解
- ArrayList執行緒不安全怎麼辦?(CopyOnWriteArrayList詳解)執行緒
- CopyOnWriteArrayList
- 剖析 CopyOnWriteArrayList
- 面試必問之 CopyOnWriteArrayList,你瞭解多少?面試
- 認識CopyOnWriteArrayList
- CopyOnWriteArrayList原始碼解析原始碼
- Java併發-CopyOnWriteArrayListJava
- Java JUC CopyOnWriteArrayList 解析Java
- java集合之CopyOnWriteArrayListJava
- HashSet、TreeSet、CopyOnWriteArraySet和CopyOnWriteArrayList
- 併發容器之CopyOnWriteArrayList
- java併發之CopyOnWriteArrayListJava
- Java併發包之 CopyOnWriteArrayListJava
- 併發包系列二—— CopyOnWriteArrayList
- jdk原始碼分析之CopyOnWriteArrayListJDK原始碼
- CopyOnWriteArrayList原始碼閱讀筆記原始碼筆記
- 【雜談】對CopyOnWriteArrayList的認識
- Java集合乾貨——CopyOnWriteArrayList原始碼分析Java原始碼
- 33-CopyOnWriteArrayList 有什麼特點?
- 死磕 java集合之CopyOnWriteArrayList原始碼分析Java原始碼
- JUC——安全容器類(CopyOnWriteArrayList,CopyOnWriteArraySet 和 ConcurrentHashMap)HashMap
- java併發資料結構之CopyOnWriteArrayListJava資料結構
- 走進原始碼——CopyOnWriteArrayList閱讀筆記原始碼筆記
- CopyOnWriteArrayList你都不知道,怎麼拿offer?
- 原始碼|併發一枝花之CopyOnWriteArrayList原始碼
- 先簡單說一說Java中的CopyOnWriteArrayListJava
- http協議/cookie詳解/session詳解HTTP協議CookieSession
- Lombok 註解詳解Lombok
- Java註解詳解Java
- Java 註解詳解Java
- Java註解最全詳解(超級詳細)Java
- HiveQL詳解Hive
- 詳解Inode
- Vuex詳解Vue
- PWA詳解
- 詳解CountDownLatchCountDownLatch