简体   繁体   中英

ConcurrentModificationError in static synchronized method

I have a class which contains a field and two static synchronized methods. The field is a map whose key is an integer and value is a list. One of the methods is used to add a new item in the list and the other one is used to read all the items in the list. I have multiple threads to read and write the map . I eliminate some details in the code so let's assume the key is always in the map.

To my understanding, only one thread can enter either write or read at the same time because both of them are declared as static synchronized . That means when one thread is writing something, the other threads cannot write or read, and vice versa. But why there is a ConcurrentModificationError thrown by the iterator iterating on the list return by map.get(i) at line for(int item: map.get(i)) ? Can anyone explain what's the reason? Thanks in advance!

class A {
   private static Map<Integer, List<Integer>> map;

   public static synchronized void write(int i, int item) {
      map.get(i).add(item);
   }

   public static synchronized void read(int i) {
      for(int item: map.get(i)) System.out.println(item);
   }
}

I think, somewhere in your code, there is a concurrent modification of the list held by the map. I wrote a small code which throws the same ConcurrentModificationException:

public class A {
    private static Map<Integer, List<Integer>> map = new HashMap<>();

    public static synchronized void write(int i, int item) {
        if (map.containsKey(i))
            map.get(i).add(item);
        else {
            ArrayList<Integer> list = new ArrayList<>();
            list.add(item);
            map.put(i, list);
        }
    }

    public static synchronized void read(int i) {
        for (int item : map.get(i))
            System.out.println(item);
    }

    public static void main(String[] args) {
        Runnable writeAction = () -> A.write(1, 1);
        Runnable readAction = () -> A.read(1);
        Runnable modifyAction = () -> A.modifyList(1, 2);

        ExecutorService service = Executors.newFixedThreadPool(4);
        for (int i = 0; i < 1_000; i++) {
            service.execute(writeAction);
            service.execute(readAction);
            service.execute(modifyAction);
        }

        service.shutdown();
    }

    /**
     * not synchronized
     * @param i
     * @param item
     */
    public static void modifyList(int i, int item) {
        if (map.containsKey(i))
            map.get(i).add(item);
        else {
            ArrayList<Integer> list = new ArrayList<>();
            list.add(item);
            map.put(i, list);
        }
    }

}

This code definitely throws the exception when two threads in the pool are running the modifyList() and read() method concurrently.

So either there is a method in your class which is not synchronized or maybe some getter which returns the ref of the original list and not copy of it (I mean escaping reference) and whichever method it is returned to - they are modifying the list and concurrently.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM