Guide to CopyOnWriteArrayList – CopyOnWriteArrayList指南

最后修改: 2017年 5月 5日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this quick article, we’ll be looking at the CopyOnWriteArrayList from the java.util.concurrent package.

在这篇简短的文章中,我们将看看CopyOnWriteArrayList,它来自java.util.concurrent包。

This is a very useful construct in the multi-threaded programs – when we want to iterate over a list in a thread-safe way without an explicit synchronization.

在多线程程序中,这是一个非常有用的结构–当我们想以线程安全的方式迭代一个列表而不需要明确的同步。

2. CopyOnWriteArrayList API

2.CopyOnWriteArrayList API

The design of the CopyOnWriteArrayList uses an interesting technique to make it thread-safe without a need for synchronization. When we are using any of the modify methods – such as add() or remove() – the whole content of the CopyOnWriteArrayList is copied into the new internal copy.

CopyOnWriteArrayList的设计使用了一种有趣的技术,使其成为线程安全的,而不需要同步化。当我们使用任何一个修改方法–例如add()remove()–时,CopyOnWriteArrayList的整个内容被复制到新的内部副本中。

Due to this simple fact, we can iterate over the list in a safe way, even when concurrent modification is happening.

由于这个简单的事实,我们可以以安全的方式迭代列表,即使是在发生并发修改的时候

When we’re calling the iterator() method on the CopyOnWriteArrayList, we get back an Iterator backed up by the immutable snapshot of the content of the CopyOnWriteArrayList.

当我们在CopyOnWriteArrayList上调用iterator()方法时,我们会得到一个Iterator,由CopyOnWriteArrayList内容的不可变快照支持。

Its content is an exact copy of data that is inside an ArrayList from the time when the Iterator was created. Even if in the meantime some other thread adds or removes an element from the list, that modification is making a fresh copy of the data that will be used in any further data lookup from that list.

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

The characteristics of this data structure make it particularly useful in cases when we are iterating over it more often than we are modifying it. If adding elements is a common operation in our scenario, then CopyOnWriteArrayList won’t be a good choice – because the additional copies will definitely lead to sub-par performance.

这种数据结构的特点使得它在我们对它的迭代比修改更频繁的情况下特别有用。如果在我们的场景中添加元素是一个常见的操作,那么CopyOnWriteArrayList将不是一个好的选择–因为额外的拷贝肯定会导致低于标准的性能。

3. Iterating Over CopyOnWriteArrayList While Inserting

3.在插入时对CopyOnWriteArrayList进行迭代

Let’s say that we’re creating an instance of the CopyOnWriteArrayList that stores integers:

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

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

Next, we want to iterate over that array, so we are creating an Iterator instance:

接下来,我们要对该数组进行迭代,所以我们要创建一个Iterator 实例。

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

After the Iterator is created, we are adding a new element to the numbers list:

在创建了Iterator之后,我们要向numbers列表添加一个新元素。

numbers.add(10);

Keep in mind that, when we create an iterator for the CopyOnWriteArrayList, we get an immutable snapshot of the data in the list at the time iterator() was called.

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

Because of that, while iterating over it, we won’t see the number 10 in the iteration:

正因为如此,在对其进行迭代时,我们不会在迭代中看到数字10

List<Integer> result = new LinkedList<>();
iterator.forEachRemaining(result::add);
 
assertThat(result).containsOnly(1, 3, 5, 8);

Subsequent iteration using newly created Iterator will also return the number 10 that was added:

随后使用新创建的Iterator进行的迭代也将返回被添加的数字10。

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

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

4. Removing While Iterating Is Not Allowed

4.不允许在迭代过程中删除

The CopyOnWriteArrayList was created to allow for the possibility of safe iterating over elements even when the underlying list gets modified.

CopyOnWriteArrayList的创建是为了允许在底层列表被修改时也能安全地迭代元素。

Because of the copying mechanism, the remove() operation on the returned Iterator is not permitted – resulting with UnsupportedOperationException:

由于复制机制,对返回的迭代器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. Conclusion

5.结论

In this quick tutorial, we had a look at the CopyOnWriteArrayList implementation from the java.util.concurrent package.

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

We saw the interesting semantics of this list and how it can be iterated in a thread-safe way, while other threads can continue inserting or removing elements from it.

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

The implementation of all these examples and code snippets can be found in the GitHub project – this is a Maven project, so it should be easy to import and run as it is.

所有这些例子和代码片段的实现都可以在GitHub项目中找到–这是一个Maven项目,所以应该很容易导入并按原样运行。