In this article, we will discuss how can we achieve both read (iterate) and modify (remove/add) operations simultaneously by 2 different threads using CopyOnWriteArrayList which isn’t possible with simple ArrayList
1. ArrayList:
If 2 different threads perform operations on same ArrayList object simultaneously, then compiler will throw ConcurrentModificationException
We will demonstrate a simple example using ArrayList which performs
- 1st thread iterating or reading element/objects one-by-one
- 2nd thread removing a particular element from List; while other thread is iterating ArrayList object
IterateAndModifyArrayListSimultaneously.java
package in.bench.resources.concurrent.collection;
import java.util.ArrayList;
// extending Thread class
public class IterateAndModifyArrayListSimultaneously extends Thread {
// creating ArrayList of type String
static ArrayList<String> alStars = new ArrayList<String>();
@Override
public void run() {
try {
// sleeping thread for 1000 ms
Thread.sleep(1000);
// removing element at 2nd position
String star = alStars.remove(1);
System.out.println("Thread 2: removed " + star);
}
catch(InterruptedException iex) {
iex.printStackTrace();
}
System.out.println("Removal is done... !!");
}
/**
* main() method
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
// adding elements to ArrayList
alStars.add("Rock Star");
alStars.add("Ultimate Star");
alStars.add("Little Star");
// creating another thread
Thread newThread = new Thread(
new IterateAndModifyArrayListSimultaneously());
newThread.start();
// iterating ArrayList using enhanced for-loop
for(String star : alStars) {
System.out.println("Thread 1 iterating : " + star);
// sleeping thread for 1500 ms, after every turn
Thread.sleep(1500);
}
System.out.println("Iterating AL completed... !!");
}
}
Output:
Thread 1 iterating : Rock Star
Thread 2: removed Ultimate Star
Removal is done... !!
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:819)
at java.util.ArrayList$Itr.next(ArrayList.java:791)
at in.bench.resources.concurrent.collection
.IterateAndModifyArrayListSimultaneously.main(
IterateAndModifyArrayListSimultaneously.java:46)
Explanation:
- Main thread iterating ArrayList and child thread removing element at 2nd position (index-1) of same ArrayList object
- From output, it is clear that while one thread is iterating on ArrayList and if any other thread perform modify operation (i.e.; on the same ArrayList object, other thread is removing an element)
- Then compiler will throw ConcurrentModificationException i.e.; it is fail-fast iterator
- Note: sleep(ms) introduced to study demo example
- Because without sleep both thread will execute independently and complete its execution in nano/pico seconds and there won’t be any compile-time error
- Since we are trying to understand with small amount of data, where execution completes in nano seconds
- But with large set of data, introduction of sleep concept isn’t required
- As execution time increases for each thread, definitely ConcurrentModificationException is thrown
Q) How to overcome above mentioned problem with ArrayList ?
- With CopyOnWriteArrayList, we can overcome this problem
- as it works on different cloned copies which is later merged into one/original copy by JVM
2. CopyOnWriteArrayList:
When 2 different threads performs operations on same CopyOnWriteArrayList object simultaneously, then compiler won’t thrown any runtime exception
This is the advantage of using CopyOnWriteArrayList over ArrayList
In the demo example,
- 1st thread iterates through all elements of CopyOnWriteArrayList
- While other thread can safely remove element at 2nd position (i.e.; index-1)
- Compiler doesn’t throws any ConcurrentModificationException unlike ArrayList
- This is because, CopyOnWriteArrayList works on separate cloned copy and later JVM merges both original & cloned copies
IterateAndModifyCopyOnWriteArrayListSimultaneously.java
package in.bench.resources.concurrent.collection;
import java.util.concurrent.CopyOnWriteArrayList;
// implementing Runnable interface
public class IterateAndModifyCopyOnWriteArrayListSimultaneously
implements Runnable {
// creating CopyOnWriteArrayList of type String
static CopyOnWriteArrayList<String> cowalStars =
new CopyOnWriteArrayList<String>();
@Override
public void run() {
try {
// sleeping thread for 1000 ms
Thread.sleep(1000);
// removing element at 2nd position
String star = cowalStars.remove(1);
System.out.println("Thread 2: removed " + star);
}
catch(InterruptedException iex) {
iex.printStackTrace();
}
System.out.println("Removal is done... !!");
}
/**
* main() method
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
// adding elements to CopyOnWriteArrayList
cowalStars.add("Rock Star");
cowalStars.add("Ultimate Star");
cowalStars.add("Little Star");
// creating another thread
Thread newThread = new Thread(
new IterateAndModifyCopyOnWriteArrayListSimultaneously());
newThread.start();
// iterating CopyOnWriteArrayList using enhanced for-loop
for(String star : cowalStars) {
System.out.println("Thread 1 iterating : " + star);
// sleeping thread for 1500 ms, after every turn
Thread.sleep(1500);
}
System.out.println("Iterating COWAL completed... !!");
}
}
Output:
Thread 1 iterating : Rock Star
Thread 2: removed Ultimate Star
Removal is done... !!
Thread 1 iterating : Ultimate Star
Thread 1 iterating : Little Star
Iterating COWAL completed... !!
Explanation:
- When we executed same program replacing ArrayList with CopyOnWriteArrayList, then program executed without any runtime exception like ConcurrentModificationException i.e.; it is fail-safe iterator
- But there might be different output at different execution point
- Because, while one thread iterating on the object it can iterate through all elements or updated items inside ArrayList which is updated by 2nd thread
- In the above example, we have got all elements of COWAL while iterating; because 2nd thread’s update/removal doesn’t reflected to 1st thread’s iteration/read
- Same isn’t true with next iteration, because next time there might be possibility of iterating through updated elements of COWAL (in this case 1st thread got updated values from 2nd thread)
Lets us print other possibility as well
Output:
Thread 2: removed Ultimate Star
Removal is done... !!
Thread 1 iterating : Rock Star
Thread 1 iterating : Little Star
Iterating COWAL completed... !!
From above output, it is clear that 1st thread got updation from 2nd thread and 1st thread iterated only on updated elements of COWAL
Related Articles:
- Java 5 – Introduction to Concurrent Collection
- Java 5 – CopyOnWriteArrayList class with example
- Java 5 – CopyOnWriteArrayList with Read and Update operations simultaneously
- Java 5 – Remove operation with CopyOnWriteArrayList and ArrayList
- Java 5 – ArrayList v/s CopyOnWriteArrayList
- Java 5 – CopyOnWriteArrayList v/s SynchronizedList
- Java 5 – Concurrent Collection Interview question and answers
References:
- https://docs.oracle.com/javase/tutorial/collections/intro/
- https://docs.oracle.com/javase/tutorial/collections/interfaces/collection.html
- https://docs.oracle.com/javase/7/docs/api/java/util/Collection.html
- https://docs.oracle.com/javase/tutorial/essential/concurrency/collections.html
- https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CopyOnWriteArrayList.html
Happy Coding !!
Happy Learning !!