1. Overview
In this quick article, we'll be looking at the CopyOnWriteArrayList from the java.util.concurrent package.
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.
這在多線程程序中是一個非常有用的結(jié)構(gòu)——當(dāng)我們想要以線程安全的方式迭代列表而不需要顯式同步時。
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 的設(shè)計使用了一種有趣的技術(shù)來使其線程安全,無需同步。 當(dāng)我們使用任何修改方法時——例如 add() 或 remove()——CopyOnWriteArrayList 的全部內(nèi)容被復(fù)制到新的內(nèi)部副本中专酗。
Due to this simple fact, we can iterate over the list in a safe way, even when concurrent modification is happening.
由于這個簡單的事實侠坎,我們可以以安全的方式迭代列表翘贮,即使發(fā)生并發(fā)修改盗誊。
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.
當(dāng)我們在 CopyOnWriteArrayList 上調(diào)用 iterator() 方法時,我們會返回一個由 CopyOnWriteArrayList 內(nèi)容的不可變快照備份的迭代器泻仙。
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.
它的內(nèi)容是創(chuàng)建迭代器時 ArrayList 中數(shù)據(jù)的精確副本。 即使同時其他線程從列表中添加或刪除元素量没,該修改也會制作數(shù)據(jù)的新副本玉转,該副本將用于從該列表中進(jìn)行任何進(jìn)一步的數(shù)據(jù)查找。
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.
這種數(shù)據(jù)結(jié)構(gòu)的特性使得它在我們迭代它比我們修改它更頻繁的情況下特別有用殴蹄。 如果在我們的場景中添加元素是一種常見的操作究抓,那么 CopyOnWriteArrayList 將不是一個好的選擇——因為額外的副本肯定會導(dǎo)致性能低于標(biāo)準(zhǔn)。
3. Iterating Over CopyOnWriteArrayList While Inserting
Let's say that we're creating an instance of the CopyOnWriteArrayList that stores integers:
假設(shè)我們正在創(chuàng)建一個存儲整數(shù)的 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:
接下來饶套,我們要迭代該數(shù)組漩蟆,因此我們正在創(chuàng)建一個 Iterator 實例:
Iterator<Integer> iterator = numbers.iterator();
After the Iterator is created, we are adding a new element to the numbers list:
創(chuàng)建迭代器后,我們向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.
請記住妓蛮,當(dāng)我們?yōu)?CopyOnWriteArrayList 創(chuàng)建迭代器時怠李,我們會在調(diào)用 iterator() 時獲得列表中數(shù)據(jù)的不可變快照。
Because of that, while iterating over it, we won't see the number 10 in the iteration:
因此蛤克,在迭代它時捺癞,我們不會在迭代中看到數(shù)字 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:
使用新創(chuàng)建的迭代器的后續(xù)迭代也將返回添加的數(shù)字 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
不允許在迭代時刪除
The CopyOnWriteArrayList was created to allow for the possibility of safe iterating over elements even when the underlying list gets modified.
創(chuàng)建 CopyOnWriteArrayList 是為了允許安全地迭代元素,即使基礎(chǔ)列表被修改
Because of the copying mechanism, the remove() operation on the returned Iterator is not permitted – resulting with UnsupportedOperationException:
由于復(fù)制機(jī)制构挤,不允許對返回的 Iterator 執(zhí)行 remove() 操作 - 導(dǎo)致 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
In this quick tutorial, we had a look at the CopyOnWriteArrayList implementation from the java.util.concurrent package.
在這個快速教程中髓介,我們查看了 java.util.concurrent 包中的 CopyOnWriteArrayList 實現(xiàn)
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.
我們看到了這個列表的有趣語義以及如何以線程安全的方式對其進(jìn)行迭代,而其他線程可以繼續(xù)從中插入或刪除元素筋现。
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.
所有這些示例和代碼片段的實現(xiàn)都可以在 GitHub 項目中找到——這是一個 Maven 項目唐础,因此應(yīng)該很容易導(dǎo)入并按原樣運(yùn)行箱歧。