Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Concurrent threads adding to ArrayList at same time - what happens?

People also ask

What will happen if multiple threads accessing the same resource?

Multiple threads accessing shared data simultaneously may lead to a timing dependent error known as data race condition. Data races may be hidden in the code without interfering or harming the program execution until the moment when threads are scheduled in a scenario (the condition) that break the program execution.

What is concurrent ArrayList?

CopyOnWriteArrayList is a thread-safe variant of ArrayList in which all mutative operations (add, set, and so on) are implemented by making a fresh copy of the underlying array. CopyOnWriteArrayList is a concurrent alternative of synchronized List implements List interface and its part of java.

Can multiple threads run at the same time in Java?

Overview. Multi-thread programming allows us to run threads concurrently, and each thread can handle different tasks. Thus, it makes optimal use of the resources, particularly when our computer has a multiple multi-core CPU or multiple CPUs.

How do you make an ArrayList concurrent?

There are two ways to create a Synchronized ArrayList.Collections. synchronizedList() method. 2. Using CopyOnWriteArrayList.


There is no guaranteed behavior for what happens when add is called concurrently by two threads on ArrayList. However, it has been my experience that both objects have been added fine. Most of the thread safety issues related to lists deal with iteration while adding/removing. Despite this, I strongly recommend against using vanilla ArrayList with multiple threads and concurrent access.

Vector used to be the standard for concurrent lists, but now the standard is to use the Collections synchronized list.

Also I highly recommend Java Concurrency in Practice by Goetz et al if you're going to be spending any time working with threads in Java. The book covers this issue in much better detail.


Any number of things could happen. You could get both objects added correctly. You could get only one of the objects added. You could get an ArrayIndexOutOfBounds exception because the size of the underlying array was not adjusted properly. Or other things may happen. Suffice it to say that you cannot rely on any behavior occurring.

As alternatives, you could use Vector, you could use Collections.synchronizedList, you could use CopyOnWriteArrayList, or you could use a separate lock. It all depends on what else you are doing and what kind of control you have over access to the collection.


You could also get a null, an ArrayOutOfBoundsException, or something left up to the implementation. HashMaps have been observed to go into an infinite loop in production systems. You don't really need to know what might go wrong, just don't do it.

You could use Vector, but it tends to work out the interface is not rich enough. You will probably find that you want a different data structure in most cases.


I came up with the following code to mimic somewhat a real world scenario.

100 tasks are run in parallel and they update their completed status to the main program. I use a CountDownLatch to wait for task completion.

import java.util.concurrent.*;
import java.util.*;

public class Runner {

    // Should be replaced with Collections.synchronizedList(new ArrayList<Integer>())
    public List<Integer> completed = new ArrayList<Integer>();

    /**
     * @param args
     */
    public static void main(String[] args) {
        Runner r = new Runner();
        ExecutorService exe = Executors.newFixedThreadPool(30);
        int tasks = 100;
        CountDownLatch latch = new CountDownLatch(tasks);
        for (int i = 0; i < tasks; i++) {
            exe.submit(r.new Task(i, latch));
        }
        try {
            latch.await();
            System.out.println("Summary:");
            System.out.println("Number of tasks completed: "
                    + r.completed.size());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        exe.shutdown();
    }

    class Task implements Runnable {

        private int id;
        private CountDownLatch latch;

        public Task(int id, CountDownLatch latch) {
            this.id = id;
            this.latch = latch;
        }

        public void run() {
            Random r = new Random();
            try {
                Thread.sleep(r.nextInt(5000)); //Actual work of the task
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            completed.add(id);
            latch.countDown();
        }
    }
}

When i ran the application 10 times and at least 3 to 4 times the program did not print correct number of completed tasks. Ideally it should print 100(if no exceptions happen). But in some cases it was printing 98, 99 etc.

Thus it proves that concurrent updates of ArrayList will not give correct results.

If i replace the ArrayList with a Synchronized version, the program outputs the correct results.


you can use List l = Collections.synchronizedList(new ArrayList()); if you want thread safe version of arrayList.


The behavior is probably undefined since ArrayList isn't threadsafe. If you modify the list while an Iterator is interating over it then you will get a ConcurrentModificationException. You can wrap the ArrayList with Collection.synchronizedList or use a thread-safe collection (there are many), or just put the add calls in a synchronized block.


You could use instead of ArrayList(); :

Collections.synchronizedList( new ArrayList() );

or

new Vector();

synchronizedList as of me preferable because it's:

  • faster on 50-100%
  • can work with already existing ArrayList's