1.概述

在这个快速文章中,我们将看的CopyOnWriteArrayList 来自java.util.concurrent的包。

这是多线程程序中非常有用的包 - 当我们想要以线程安全的方式迭代列表而没有显式同步时。

2. CopyOnWriteArrayList API

CopyOnWriteArrayList的设计使用一种有趣的技术使其成为线程安全的,无需同步。当我们使用任何修改方法时 - 例如add()或remove() -将CopyOnWriteArrayList的全部内容复制到新的内部副本中。

由于这个简单的事实,我们可以安全地迭代列表,即使正在进行并发修改。

当我们在CopyOnWriteArrayList上调用iterator()方法时,我们返回一个由CopyOnWriteArrayList内容的不可变快照备份的Iterator。

它的内容是从创建Iterator时开始在ArrayList中的数据的精确副本。即使在此期间某个其他线程从列表中添加或删除了一个元素,该修改也会生成一个新的数据副本,该数据将用于该列表的任何进一步数据查找。

这种数据结构的特性使它特别适用于我们迭代它而不是修改它的情况。如果在我们的场景中添加元素是一个常见的操作,那么CopyOnWriteArrayList将不是一个好的选择 - 因为额外的副本肯定会导致低于标准的性能。

3. 插入时迭代CopyOnWriteArrayList

假设我们正在创建一个存储整数的CopyOnWriteArrayList实例:

CopyOnWriteArrayList<Integer> numbers 
  = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 8});

接下来,我们想迭代该数组,因此我们创建了一个Iterator实例:

Iterator<Integer> iterator = numbers.iterator();

创建Iterator后,我们将向数字列表添加一个新元素:

numbers.add(10);

请记住,当我们为CopyOnWriteArrayList创建一个迭代器时,我们会在调用iterator()时获得列表中数据的不可变快照。

因此,在迭代它时,我们不会在迭代中看到数字10:

List<Integer> result = new LinkedList<>();
iterator.forEachRemaining(result::add);

assertThat(result).containsOnly(1, 3, 5, 8);

使用新创建的迭代器进行的后续迭代将返回添加的数字10:

Iterator<Integer> iterator2 = numbers.iterator();
List<Integer> result2 = new LinkedList<>();
iterator2.forEachRemaining(result2::add);

assertThat(result2).containsOnly(1, 3, 5, 8, 10);

4.在不允许迭代时删除

该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();
  }
}

5.结论

在这个快速教程中,我们看了一下java.util.concurrent包中的CopyOnWriteArrayList实现。

我们看到了这个列表的有趣语义以及它如何以线程安全的方式进行迭代,而其他线程可以继续插入或删除它中的元素。


关注公众号:「Java知己」,每天更新Java知识哦,期待你的到来!

  • 发送「1024」,免费领取 30 本经典编程书籍。
  • 发送「Group」,与 10 万程序员一起进步。
  • 发送「JavaEE实战」,领取《JavaEE实战》系列视频教程。
  • 发送「玩转算法」,领取《玩转算法》系列视频教程。

image

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐