19
 public synchronized X getAnotherX(){ 
  if(iterator.hasNext()){
   X b = iterator.next();
   String name = b.getInputFileName();
  ...
   return b;
  }
  else{return null;}
 }

despite the synchronized statement in the declaration header, i still get a ConcurrentModificationException Exception at the line where i use iterator.next(); whats wrong here ?

Raedwald
  • 46,613
  • 43
  • 151
  • 237
dayscott
  • 457
  • 1
  • 6
  • 17
  • 1
    See http://stackoverflow.com/questions/223918/iterating-through-a-collection-avoiding-concurrentmodificationexception-when-re – Raedwald Mar 28 '16 at 14:26
  • Possible duplicate of [Why is a ConcurrentModificationException thrown](https://stackoverflow.com/questions/602636/why-is-a-concurrentmodificationexception-thrown) – Raedwald Mar 13 '19 at 12:00

3 Answers3

44

ConcurrentModificationException usually has nothing to do with multiple threads. Most of the time it occurs because you are modifying the collection over which it is iterating within the body of the iteration loop. For example, this will cause it:

Iterator iterator = collection.iterator();
while (iterator.hasNext()) {
    Item item = (Item) iterator.next();
    if (item.satisfiesCondition()) {
       collection.remove(item);
    }
}

In this case you must use the iterator.remove() method instead. This occurs equally if you are adding to the collection, in which case there is no general solution. However, the subtype ListIterator can be used if dealing with a list and this has an add() method.

Langusten Gustel
  • 10,917
  • 9
  • 46
  • 59
Ramon
  • 8,202
  • 4
  • 33
  • 41
  • 1
    i don't get it, i simply want a String which is (in this case in object called "b") . but well i tried to use iterator.remove(); but that didn't helped. same exception coming. – dayscott Oct 31 '09 at 19:54
  • It would be helpful if you posted your entire code for the method – Ramon Oct 31 '09 at 20:11
  • 1
    no problem: public synchronized Block getAnotherBlock(){ Block b = null; if(iterator.hasNext()){ b = iterator.next(); iterator.remove(); } String name = b.getInputFileName(); Integer []arr = blocksPerFileLeft.get(name); arr[1] += 1; blocksPerFileLeft.put(b.getInputFileName(), arr); currBlockPos++; //incr global var return b; – dayscott Oct 31 '09 at 21:17
  • Why not make the Iterator a local variable? – Ramon Oct 31 '09 at 22:02
  • i need it to be a object variable because the method getAnotherBlock() wants be called from different threads, so it can't be a local variable of the method. behind the iterator there is actually a linked list, so i could try to make the iterator local, like you proposed and call the remove() statement everytime, hmm - but i still don't get why my posted code doesn't work : / (how to mark code in a commment around here by the way, when i say " add comment there is no bar where i can highlight code snippets : / ) – dayscott Nov 01 '09 at 11:13
  • Ok I see that in your case it actually is a threads issue so probably more complicated. Yeah then my feeling is the Iterator should definitely be a local variable, since they represent the current position in the list it is highly unusual to keep them outside the scope of a method. – Ramon Nov 01 '09 at 14:30
  • "since they represent the current position in the list it is highly unusual to keep them outside the scope of a method" but what do you do if you want to represent the current position in a list within a living object, not just a single method, than you have to put the iterator as a local variable of the object, don't you ? so what do you exactly mean when you say "it is highly unusul" ? how is this kind of problem solved then "in reality" ? ( i am not a very experienced programmer, so i hope you don't mind if i ask ) – dayscott Nov 02 '09 at 22:57
  • 1
    Ok, thats fine but in this case *any* modification to the list after you have taken out the iterator (i.e. called the `iterator()` method) will result in `ConcurrentModificationException` even if every access to the collection is synchronized. You cannot interleave calls to iterator methods with mutations to the collection. To see the reason for this, think about how the iterator is implemented and what you expect to happen if someone inserts or removes elements before or after the iterator's current position. – Ramon Nov 03 '09 at 06:43
  • 1
    +1 on the statement : `ConcurrentModificationException usually has nothing to do with multiple threads. Most of the time it occurs because you are modifying the collection over which it is iterating within the body of the iteration loop`. That is the first thing I say to myself when confronted with this error – Jimmy Nov 11 '10 at 15:09
  • @Ramon: I think it is better to replace that collection.remove(item); line by iterator.remove() – Bikash Gyawali Jun 25 '11 at 18:33
  • 1
    I do think its mostly happens due to multi-threading. If one thread is modifying, while another already iterating. Great if you can avoid using iterator though. Iterator is like creating a snapshot while iterator created, then keep checking if the collection modified. – Jackie Jun 02 '13 at 14:48
  • According to JSR, this is not guaranteed actually. Better to make it explicitly no concurrent modification happens. – Jackie Jul 17 '13 at 15:17
  • I still get this exception time to time even if I use iterator.remove() function. – tasomaniac Apr 29 '14 at 11:13
  • After reading this: "Most of the time it occurs because you are modifying the collection over which it is iterating within the body of the iteration loop." I found my stupid bug. Thanks. – John John Pichler Sep 14 '14 at 14:04
6

I agree with the statements above about ConcurrentModificationException often happening as a result of modifying the collection in the same thread as iterating. However, it is not always the reason.

The thing to remember about synchronized is that it only guarantees exclusive access if everybody accessing the shared resource also synchronizes.

For example, you can synchronize access to a shared variable:

synchronized (foo) {
  foo.setBar();
}

And you can think that you have exclusive access to it. However, there is nothing to stop another thread just doing something without the synchronized block:

foo.setBar();  // No synchronization first.

Through bad luck (or Murphy's Law, "Anything that can go wrong, will go wrong."), these two threads can happen to execute at the same time. In the case of structural modifications of some widely-used collections (e.g. ArrayList, HashSet, HashMap etc), this can result in a ConcurrentModificationException.

It is hard to prevent the problem entirely:

  • You can document synchronization requirements, e.g. inserting "you must synchronize on blah before modifying this collection" or "acquire bloo lock first", but that's relying upon users to discover, read, understand and apply the instruction.

    There is the javax.annotation.concurrent.GuardedBy annotation, which can help to document this in a standardized way; the problem is then that you have to have some means of checking correct use of the annotation in the toolchain. For example, you might be able to use something like Google's errorprone, which can check in some situations, but it's not perfect.

  • For simple operations on collections, you can make use of the Collections.synchronizedXXX factory methods, which wrap a collection so that every method call synchronizes on the underlying collection first, e.g. the SynchronizedCollection.add method:

    @Override public boolean add(E e) {
      synchronized (mutex) { return c.add(obj); }
    }
    

    Where mutex is the synchronized-on instance (often the SynchronizedCollection itself), and c is the wrapped collection.

    The two caveats with this approach are:

    1. You have to be careful that the wrapped collection cannot be accessed in any other way, since that would allow non-synchronized access, the original problem. This is typically achieved by wrapping the collection immediately on construction:

      Collections.synchronizedList(new ArrayList<T>());
      
    2. The synchronization is applied per method call, so if you are doing some compound operation, e.g.

      if (c.size() > 5) { c.add(new Frob()); }
      

      then you don't have exclusive access throughout that operation, only for the size() and add(...) calls individually.

      In order to get mutually exclusive access for the duration of the compound operation, you would need to externally synchronize, e.g. synchronized (c) { ... }. This requires you to know the correct thing to synchronize on, however, which may or may not be c.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
0

Below example is just the demo for this:

public class SynchronizedListDemo {
public static void main(String[] args) throws InterruptedException {
    List<Integer> list = new ArrayList<>();
    for(int i=0;i<100000;i++){
        System.out.println(i);
        list.add(i);
    }
    // Synchronzied list will also give ConcurrentModificationException, b'coz
    // it makes only methods thread safe, but you are still modifying list while iterating it.
    // You can use 'ListIterator' or 'CopyOnWriteArrayList'
    List<Integer> list1 = Collections.synchronizedList(list);

    Runnable r1= ()->{
        for(Integer i: list1)
            System.out.println(i);
    };
    Runnable r2 = ()->{
        try {
            System.out.println();
            System.out.println("Removing....");
            //list1.add(4); // Will give ConcurrentModificationException
            System.out.println("Removed");
        } catch (Exception e) {
            e.printStackTrace();
        }
    };

    // This will not give ConcurrentModificationException as it work on the copy of list.
    List<Integer> list2 = new CopyOnWriteArrayList<>(list);

    Runnable r3= ()->{
        for(Integer i: list2)
            System.out.println(i);
    };
    Runnable r4 = ()->{
        try {
            System.out.println();
            System.out.println("Removing....");
            list2.add(4);
            System.out.println("Removed");
        } catch (Exception e) {
            e.printStackTrace();
        }
    };

    Thread t1 = new Thread(r3);
    Thread.sleep(100);
    Thread t2 = new Thread(r4);
    t1.start();
    t2.start();

    System.out.println("Done");
}

}

Akash5288
  • 1,919
  • 22
  • 16