在这篇文章中,咱们将查看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();
}
}
复制代码