1. Overview
In this article, we will learn about the Java CopyOnWriteArrayList. To learn more about Java, refer to these articles.
2. Java CopyOnWriteArrayList
This is a thread-safe variant of the ArrayList
, introduced in JDK 5 as part of the java.util.concurrent
package. This implements the List
interface and is useful for handling concurrent executions.
It allows all elements including null.
2.1. Mutable operations
All mutative operations (add, set, and so on) are implemented by making a fresh copy of the underlying array. For example, the following add
method of the CopyOnWriteArrayList
creates an internal copy of the array, adds the new element and later updates the actual array.
public boolean add(E e) { synchronized (lock) { Object[] es = getArray(); int len = es.length; es = Arrays.copyOf(es, len + 1); es[len] = e; setArray(es); return true; } }
The lock usage in this code provides memory ordering among threads that use the same lock. Specifically, the unlock at the end of this method provides happens-before semantics with other threads that gain the same lock.
The getArray
method does a volatile read of this field, and the setArray
method does a volatile write of this field. This guarantees visibility of changes made from one thread to another also as “happens-before” which solves the problem of memory writes that happen in one thread can “leak through” and be seen by another thread.
You can refer this stackoverflow link to understand more on this memory model.
2.2. When to use CopyOnWriteArrayList
This is ordinarily too costly, but may be more efficient than alternatives when read or traversal operations vastly outnumber mutations or alterations such as add, set, update. It is useful when you cannot or don’t want to synchronize traversals, yet need to prevent interference among concurrent threads.
2.3. Java CopyOnWriteArrayList iterator
The “snapshot” style iterator method uses a reference to the state of the array at the point that the iterator was created. This array never changes during the lifetime of the iterator, so interference is impossible, and the iterator is guaranteed not to throw ConcurrentModificationException
.
The iterator will not reflect additions, removals, or changes to the list since the iterator was created.
@Test void testCopyOnWriteArrayList() { CopyOnWriteArrayList<String> al = new CopyOnWriteArrayList<String>(); System.out.println("Initial size of al: " + al.size()); al.add("C"); al.add("A"); al.add("E"); al.add("B"); al.add("D"); System.out.println("Contents of al: " + al); try { Iterator<String> iterator = al.iterator(); al.add("Z"); System.out.println("Contents of al: " + al); while(iterator.hasNext()) { System.out.println("printing... " + iterator.next()); } } catch(UnsupportedOperationException e) { System.out.println("Method not supported:"); } System.out.println("Size of al: " + al.size()); }
We have added ‘Z’ after creating the iterator. So iterator doesn’t reflect the value ‘Z’ while printing the results.
Contents of al: [C, A, E, B, D] Contents of al: [C, A, E, B, D, Z] printing... C printing... A printing... E printing... B printing... D Size of al: 6
Element-changing operations on iterators themselves (remove, set, and add) are not supported. These methods throw UnsupportedOperationException
.
try { Iterator<String> iterator = al.iterator(); al.add("Z"); System.out.println("Contents of al: " + al); while(iterator.hasNext()) { System.out.println("printing... " + iterator.next()); iterator.remove(); } } catch(UnsupportedOperationException e) { System.out.println("Method not supported:"); }
In the above code, we are trying to remove element using the iterator. This is not permitted as the underlying data set of Iterator
is not modifiable.
Contents of al: [C, A, E, B, D, Z] printing... C Method not supported:
3. Conclusion
To sum up, we have learned about the Java’s concurrent list CopyOnWriteArrayList.