0

I have logic similar to below in my program which throws ConcurrentModificationException. How can I make java.util.ArrayList threadsafe other than using CopyOnWriteArrayList (because I have large number of write on this list and as per my understanding writes are expensive on CopyOnWriteArrayList)

import java.util.ArrayList;
import java.util.List;

public class Test extends Thread {

    List<String> list = new ArrayList<String>();

    public static void main(String[] args) {
        Test t = new Test();
        t.start();

        System.out.println("in main thred");

        t.destory();

    }

    public void destory() {
        list.stream().toArray(String[]::new);
        System.out.println("done");

    }

    @Override
    public void run() {
        while(true) {
            list.add("test");
        }

    }
}

Output:

in main thred
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1388)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:546)
    at java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260)
    at java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:438)
acg
  • 179
  • 3
  • 13
  • https://stackoverflow.com/questions/2444005/how-do-i-make-my-arraylist-thread-safe-another-approach-to-problem-in-java – Sotirios Delimanolis May 14 '20 at 17:14
  • https://stackoverflow.com/questions/38845778/thread-safety-when-iterating-through-an-arraylist-using-foreach – Sotirios Delimanolis May 14 '20 at 17:15
  • 4
    Does this answer your question? [How do I make my ArrayList Thread-Safe? Another approach to problem in Java?](https://stackoverflow.com/questions/2444005/how-do-i-make-my-arraylist-thread-safe-another-approach-to-problem-in-java) – Dracontis May 14 '20 at 18:48

1 Answers1

1

You have to synchronize all accesses to the list:

public void destory() {
    synchronized (list) {
      list.stream().toArray(String[]::new);
    }
    System.out.println("done");

}

and

    while(true) {
      synchronized (list) {
        list.add("test");
      }
    }

Note that it is not sufficient simply to wrap the list in Collections.synchronizedList, since that won't hold the monitor for the entirety of the iteration by the stream.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • 2
    But there is no need for using the Stream API here. When replacing the `list.stream().toArray(String[]::new);` with `list.toArray(new String[0]);`, using `synchronizedList` would work. – Holger May 15 '20 at 12:26