在這篇文章中,咱們將查看java.util.concurrent包中的CopyOnWriteArrayList。java
CopyOnWriteArrayList的設計使用一種有趣的技術使其成爲線程安全的,無需同步。當咱們使用任何修改方法時,例如add()或remove(),CopyOnWriteArrayList的所有內容將複製到新的內部副本中。安全
基於這個緣由,咱們能夠線程安全地迭代列表,即便當前有併發修改發生。 當咱們在CopyOnWriteArrayList上調研iterator()方法時,咱們返回一個由CopyOnWriteArrayList內容的不可變快照備份的Iterator。bash
其內容是從建立Iterator時開始在ArrayList中的數據的完整副本。即便在此期間其餘線程添加或刪除列表中的元素,該修改也會生成數據的新副本,該副本將用於從該列表進行的任何進一步數據查找。數據結構
這種數據結構的特性使它特別適用於咱們迭代它而不是修改它的狀況,若是添加元素是咱們場景中的常見操做,那麼CopyOnWriteArrayList將不是一個好的選擇 - 由於額外的副本確定會致使低於標準的性能。併發
首先建立一個存儲整數的CopyOnWriteArrayList。性能
CopyOnWriteArrayList<Integer> numbers
= new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 8});
複製代碼
接着,建立一個迭代器spa
Iterator<Integer> iterator = numbers.iterator();
複製代碼
最後,咱們向list追加一個元素線程
numbers.add(10);
複製代碼
因爲咱們建立迭代器後,咱們拿到了數據的副本。所以,當咱們迭代時,咱們將看不到10這個值。設計
List<Integer> result = new LinkedList<>();
iterator.forEachRemaining(result::add);
assertThat(result).containsOnly(1, 3, 5, 8);
複製代碼
再建立一個迭代器後,就可可拿到咱們後來追擊的10這個值。code
Iterator<Integer> iterator2 = numbers.iterator();
List<Integer> result2 = new LinkedList<>();
iterator2.forEachRemaining(result2::add);
assertThat(result2).containsOnly(1, 3, 5, 8, 10);
複製代碼
建立了CopyOnWriteArrayList是爲了容許即便在底層列表被修改時也能夠安全地迭代元素。
由於是複製機制,因此不容許對返回的迭代器執行remove()操做,會致使UnsupportedOperationException:
@Test(expected = UnsupportedOperationException.class)
public void whenIterateOverItAndTryToRemoveElement_thenShouldThrowException() {
CopyOnWriteArrayList<Integer> numbers
= new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 8});
Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
iterator.remove();
}
}
複製代碼