Concurrent Threads Adding to Arraylist at Same Time - What Happens

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

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.

Java ArrayList.add() method thread safe for purely parallel adding?

No, it's not thread-safe. Wrap your list using Collections.synchronizedList(), or use explicit synchronization when accessing the list.

Synchronized List - add multiple threads and remove one thread

It appears to me that List is not the class you're looking for. Java actually has a Queue class (and a thread safe implementation of it).

Then, looking further into your code, I can see that it's very wasteful;
you're creating a copy of the List, removing the object from the original and then printing the copy.
That seems like a CopyOnWriteArrayList but less thread safe to me. This can be useful, but the Queue solution would be best suitable in this case and more performant.

This is what your code would look like after changing it to use BlockingQueue:

private static boolean flagStop = false;

//synchronized list
private static Queue<Object> queue = new BlockingQueue<Object>();

//thread which check once per second the size of queue
//and print elements from list and try to remove them safely
private static Thread printQueueThread = new Thread(() -> {
Object currentObject;
while(!flagStop) {
while((object = queue.poll()) != null) {
System.out.println(currentObject);
}
}
try {
Thread.sleep(1_000);
} catch (Exception ignored) {
}
}
});

static{
printQueueThread.start();
}

//add elements in queue
//this is accessed by multiple threads, by few times per second.
public static void addElement(Object obj){
queue.offer(obj);
}

On the other hand, you're also using a boolean to tell your thread whether it should stop or not. That can cause synchronization issues. You should really replace it with AtomicBoolean instead - a thread safe implementation of boolean.
Changing boolean flagStop = false to AtomicBoolean flagStop = new AtomicBoolean() and while (!flagStop) to while(!flagStop.get()).

I'm not really sure as to why your class is also all static, but that's generally bad practice. I would avoid that and use instances.
I would also suggest implementing Runnable, making the code more clear and adding a null check in the addElement.
With all the corrections your final class would look like this:

public class MyPrintQueue implements Runnable {
private AtomicBoolean shouldStop;

//synchronized list
private Queue<Object> queue;

//thread which check once per second the size of queue
//and print elements from list and try to remove them safely
private Thread printQueueThread;

public MyPrintQueue() {
this.queue = new BlockingQueue<>();
this.printQueueThread = new Thread(this);
}

@Override
public void run() {
Object currentObject;
while(!flagStop.get()) {
while((object = queue.poll()) != null) {
System.out.println(currentObject);
}
}
try {
Thread.sleep(1_000);
} catch (Exception ignored) {
// Ignored
}
}
}

public void startThread() {
printQueueThread.start();
}

public void stop() {
flagStop.set(true);
}

//add elements in queue
//this is accessed by multiple threads, by few times per second.
public void addElement(Object obj){
if (obj == null)
return;
queue.offer(obj);
}
}

That being said, your code doesn't seem completely thread safe to me. I might be wrong, but you could remove the Thread.sleep call and create a TestClass which calls addElement() in a while true to check if any exception is ever thrown.
If you don't want to use Queue and must use a List, you should then replace Collections.synchronizedList with a CopyOnWriteArrayList<>(), effectively changing your code to this

private static boolean flagStop = false;

//synchronized list
private static List<Object> queue = new CopyOnWriteArrayList<>();

//thread which check once per second the size of queue
//and print elements from list and try to remove them safely
private static Thread printQueueThread = new Thread(() -> {

while(!flagStop){

if(!queue.isEmpty()){
while (queue.size() > 0) {
System.out.println(queue.get(0));
queue.remove(0);
}
}

try{ Thread.sleep(1_000); }catch(Exception ignored){}

}

});

static{
printQueueThread.start();
}

//add elements in queue
//this is accessed by multiple threads, by few times per second.
public static void addElement(Object obj){
queue.add(obj);
}

Note that the code above is still static (!) and does not implement AtomicBoolean.
Also take a look at the following question and its answers which seem to be related to yours: Is externally synchronized ArrayList thread safe if its fields are not volatile?

While adding element into ArrayList using Multithreading, sometimes it give ConcurrentModificationException and sometimes not?

Please see my answer inline in your code:

import java.util.ArrayList;
import java.util.Iterator;

public class ConcurrentDemo extends Thread{
static ArrayList l=new ArrayList();
public void run()
{

System.out.println("child thread updating list");
l.add("D");
System.out.println(l);

}

public static void main(String args[]) throws InterruptedException
{

//----> Main thread starts here
l.add("A");
l.add("B");
l.add("c");

//----> l now contains A,B,C

ConcurrentDemo c=new ConcurrentDemo();

//----> You have started a second thread here
c.start();

//-----> Its not determined, which line will be executed first from now on, as 2 threads are running parallelly, the ConcurrentModificationException most likely occur in cases, when the "l.add("D");" called within the "run();" method AFTER the Iterator has been created.

System.out.println(l);
Iterator itr =l.iterator();
while(itr.hasNext())
{
String s1=(String)itr.next();
System.out.println("main thread list:" + s1);
Thread.sleep(3000);
}
System.out.println(l);
}
}

Please note regarding to interators, that the behavior of an iterator is unspecified if the underlying collection is modified while the iteration is in progress in any way other than by calling the appropriate method on the Iterator interface.Reference

Instead of randomly failing when you do this, the collection is nice
enough to keep track of how many times it's been modified, and throw
ConcurrentModificationException when it detects concurrent
modification. Reference

If you plan to modify the underlying collection of an iterator by adding new elements, consider using the ListIterator

Example with your code:

  static ArrayList l=new ArrayList();
ListIterator listItr =l.listIterator();
listItr.add(e);

For further informations, check out this Java Concurrency and Multithreading tutorial.

EDIT:
As it might be hard to notice, I am highlighting the most important inline comment within the code above:

After you have called c.start(); its not determined, which line will be executed first, as 2 threads are running parallelly, the ConcurrentModificationException most likely occur in cases, when the l.add("D"); called within the run(); method after the Iterator has been created.

Multi Threading with ArrayList() got ArrayIndexOutOfBoundsException

ArrayList is not a thread-safe class.

Underlying storage for elements is Object[] which is an array. Any array requires the allocation space in memory which is predefined in the compile time. However, when an ArrayList "wants" to add the new element (beyond the underlying array bound), several things have to be done (without your knowledge). Underlying array gets a new (increased) length. Every element of the old array is copied to the new array, and then the new element is added. So, you can expect the ArrayIndexOutOfBoundsException exception when an ArrayList is used in multi-thread environment.

You are adding elements too fast so ArrayList#add() -> grow() -> newCapacity() can't calculate the correct capacity to allocate the memory for all of the elements coming in.

private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}

At some point of time, the condition s == elementData.length inside ArrayList#add says that there is a space for a new element A. Immediately after that other threads put their elements into the list. Now there is no space for A and elementData[s] = e; throws an exception.

Two threads accessing the same ArrayList at the same time?

The answer to the question is that it depends. I assume there is other chunk of code that increments the currentQueue variable. This being the case, the lock is happening not at the 'currentQueue' variable and neither is it happening at the collection of 'queues', but rather it is happening on one of the 10 queues (or however many you have) in the 'queues' collection.

Hence, if both threads happen to access the same queue (say queue 5), then the answer to your question is yes. However, for that to happen is one in ten chance (one in x chance, where x = the number or queues in the 'queues' collection). Therefore, if the threads access different queues, then the answer is no.



Related Topics



Leave a reply



Submit