Monday, 7 September 2015

How and why to synchronize ArrayList in Java

ArrayList is one of the most used Collections and why not? It's easy to use, has many implemented methods to provide all the important functionality and it is fast. But that fast performance comes at a cost, ArrayList is not synchronized. What does that mean? That means sharing an instance of arrayList among many threads where those threads are modifying (by adding or removing the values) the collection may result in unpredictable behaviour.
So in this post we'll see why we may need to synchronize an arraylist and how to synchronize an arraylist.

First of all let's see what will happen if an instance of arraylist is used among many threads. In the given code an arraylist instance is shared among three threads and each thread is trying to insert ten elements in the arraylist. So the expected output is - Size of arraylist should be 30 and on looping the list I should get values 0..9 three times. As I said it may give unpredictable result so let's see what happens when we run it.

Example Code

public class SynchroProblem implements Runnable{
    private List<Integer> numList;
    
    //Constructor
    public SynchroProblem(List<Integer> numList){
        this.numList = numList;
    }
    @Override
    public void run() {
        System.out.println("in run method");
        for(int i = 0; i < 10; i++){
            numList.add(i);
            try {
                // introducing some delay
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        List<Integer> numList = new ArrayList<Integer>();
        // Creating three threads
        Thread t1 = new Thread(new SynchroProblem(numList));
        Thread t2 = new Thread(new SynchroProblem(numList));
        Thread t3 = new Thread(new SynchroProblem(numList));
        t1.start();
        t2.start();
        t3.start();
        try {
            t1.join();
            t2.join();
            t3.join();
        } catch (InterruptedException e) {    
            e.printStackTrace();
        }
        System.out.println("Size of list is " + numList.size());
        for(Integer i : numList){
            System.out.println("num - " + i);
        }    
    }
}

Output

in run method
in run method
in run method
Size of list is 27
num - null
num - 0
num - 0
num - 1
num - 1
num - 1
num - 2
num - 2
num - 2
num - 3
num - null
num - 3
num - 4
num - 4
num - 4
num - 5
num - 5
num - 5
num - 6
num - 6
num - 7
num - 7
num - 7
num - 8
num - 9
num - 9
num - 9

It can be seen from the output that size is 27 (not 30) and two of the values are NULL where as 6 is inserted only twice and 8 only one time. So you can see the effect of multi-threading on an arraylist. Note that if you run this program on your system you may get different result. Well it is supposed to be unpredictable!

Alternatives

Now when we know ArrayList is not synchronized and sharing its instance among many threads may give unpredictable result we have to look for the alternatives. We can of course use a Vector which is synchronized, Collections class also provides a method synchronizedList, which returns a synchronized (thread-safe) list backed by the specified list. Another alternative is CopyOnWriteArrayList which is part of the java.util.concurrent Package.
Let's see the same example used above with Collections.synchronizedList.

public class SynchroProblem implements Runnable{
    private List<Integer> numList;
    
    //Constructor
    public SynchroProblem(List<Integer> numList){
        this.numList = numList;
    }
    @Override
    public void run() {
        System.out.println("in run method");
        for(int i = 0; i < 10; i++){
            numList.add(i);
            try {
                // introducing some delay
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        //List<Integer> numList = new Vector<Integer>();
        // Synchronizing the list
        List<Integer> numList = Collections.synchronizedList(new ArrayList<Integer>());
        // Creating three threads
        Thread t1 = new Thread(new SynchroProblem(numList));
        Thread t2 = new Thread(new SynchroProblem(numList));
        Thread t3 = new Thread(new SynchroProblem(numList));
        t1.start();
        t2.start();
        t3.start();
        try {
            t1.join();
            t2.join();
            t3.join();
        } catch (InterruptedException e) {    
            e.printStackTrace();
        }
        System.out.println("Size of list is " + numList.size());
        for(Integer i : numList){
            System.out.println("num - " + i);
        }    
    }
}

Output

Size of list is 30

Now everytime I get the size of the list as 30. Also elements are displayed properly 0..9 three times (I have omitted the display here).
We can also use the Vector class. It is already there in the code just uncomment the line with the vector and comment the Collections.synchronized line to see how it works with vector class. Note that Vector class is also retrofitted to use List interface that is why List reference is used to hold the Vector class.

Iterating a list in multi-threaded environment

While iterating an arraylist in a multi-threaded environment there may be a chance that another thread may remove a value while one thread is iterating over a list which will disrupt the serial access.

In that case also we need to synchronize the list, In order to guarantee serial access, it is critical that all access to the backing list is accomplished through the returned list. It is imperative that the user manually synchronize on the returned list when iterating over it. Failure to follow this advice may result in non-deterministic behavior.
Reference : http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#synchronizedList-java.util.List-

List list = Collections.synchronizedList(new ArrayList());
      ...
  synchronized (list) {
      Iterator i = list.iterator(); // Must be in synchronized block
      while (i.hasNext())
          foo(i.next());
  }

That's all for this topic How and why to synchronize ArrayList in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. How ArrayList works internally in Java
  2. How to sort arraylist in Java
  3. How to sort arraylist of custom objects in Java
  4. How to remove elements from an ArrayList in Java
  5. CopyOnWriteArrayList in Java
  6. Synchronization in Java multithreading
  7. Java Collections interview questions

You may also like -

1 comment:

  1. Thanks for explaining it with a nice example!

    ReplyDelete