Search code examples
javajava.util.concurrent

Java ConcurrentModificationException for writing to array list from multiple thread


In java I have the following Item class

public class Item {
    private final List<Integer> list = new ArrayList<>();

    public synchronized List<Integer> getList() {
        return list;
    }
}

and I'm accessing list element of this class from multiple threads which is throwing concurrent exception error.

public class Main {

    public static void main(String[] args) {
        Item instance1 = new Item();
        Collections.addAll(instance1.getList(),-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15);

        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 1;i<=25;i++){
                    instance1.getList().add(i);
                }
                for(int i : instance1.getList()){
                    System.out.println(i+ " ");
                }

                thirdPartyLib(instance1.getList());//Third party library which is internally iterating over list
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 26;i<=50;i++){
                    instance1.getList().add(i);
                }
                for(int i : instance1.getList()){
                    System.out.println("from2 "+i);
                }

               thirdPartyLib(instance1.getList());//Third party library which is internally iterating over list
            }
        }).start();
    }
}

It is perfectly working with CopyOnWriteArrayList but in real scenario list is being edited many times from multiple places therefore CopyOnWriteArrayList is not the good choice for me. Also I can't use Iterator because this error is thrown by third party library which is working with list. is there any way by which I can solve this error?


Solution

  • There exists Collections.synchronizedList, however you would still need to synchronize on the list when using an Iterator, so the third party lib is problematic.

        new Thread(() -> {
            List<Integer> list = instance1.getList();
            for(int i = 1;i<=25;i++){
                list.add(i);
            }
            synchronized (list) {
                for(int i : list){
                    System.out.println(i+ " ");
                }
            }
            synchronized (list) {
                thirdPartyLib(list);//Third party library which is internally iterating over list
            }
        }).start();
    
        new Thread(() -> {
            List<Integer> list = instance1.getList();
            for(int i = 26;i<=50;i++){
                instance1.list.add(i);
            }
            synchronized (list) {
                for(int i : list){
                    System.out.println("from2 "+i);
                }
            }
            synchronized (list) {
                thirdPartyLib(list);//Third party library which is internally iterating over list
            }
        }).start();
    }
    
    public class Item {
        private final List<Integer> list = Collections.synchronizedList(new ArrayList<>());
    
        public List<Integer> getList() {
            return list;
        }
    }
    

    This is quite unsatisfactory.

    You could combine that CopyOnWriteArrayList and provide independent IntStreams for iterations, best with no List access, just an add method in Item.