1

I got an final ArrayList<RoutingTableEntry> routingTable = new ArrayList<>(); which is accessed multiple times. But I only get at one Point an ConcurrentModificationException which is in the following Thread:

Thread checkReplies = new Thread(() -> {

    while (true) {

        synchronized (routingTable) {

            for (RoutingTableEntry entry : routingTable) { // throws it here

                // do smth
            }
        }

        // [...]
    }
 });
 checkReplies.start();

It throws the exception at the loop even though the routingTable is already synchronized. This thread gets only executed once per class.

Any ideas?

7
  • Are you adding/removing items in routingTable inside the for cycle ? Commented Aug 22, 2019 at 7:55
  • Is there a particular reason why you don't just use Collections.synchronizedList() or CopyOnWriteArrayList? Commented Aug 22, 2019 at 7:57
  • @Joe iterating a Collections.synchronizedList isn't the same as iterating a list in a synchronized block. Commented Aug 22, 2019 at 8:02
  • @GabrieleMariotti i call a method which removes then (should i make the method synchronized?) Commented Aug 22, 2019 at 8:07
  • @Stefanxyz This is the issue. You can't do it. Commented Aug 22, 2019 at 8:09

3 Answers 3

2

There are two possibilities:

  1. You have other code in the class that modifies routingTable, and doesn't use synchronized (routingTable) when doing so. So when the other code modifies the list during that iteration, you get the error.

  2. You're modifying the list where you have the comment "do smth". Just because you have have the list synchronized, that doesn't mean you can modify it while looping through with its iterator. You can't (except through the iterator itself, which would mean you couldn't use the enhanced for loop). (Sometimes you get away with it because of the details of the ArrayList implementation, but other times you don't.)

Here's an example of #2 (live copy):

var routingTable = new ArrayList<String>();
routingTable.add("one");
routingTable.add("two");
routingTable.add("three");
synchronized (routingTable) {
    for (String entry : routingTable) {
        if (entry.equals("two")) {
            routingTable.add("four");
        }
    }
}

That fails with JDK12's implementation of ArrayList (at least, probably others).

One key thing to understand is that synchronization and modifying the list during iteration are largely unrelated concepts. Synchronization (done properly) prevents multiple threads from accessing the list at the same time. But as you can see in the example above, just a single thread can cause a ConcurrentModificationException by modifying the list during the iteration. They only relate in that if you have one thread reading the list and another thread that may modify it, synchronization prevents the modification while the read is happening. Other than that, they're unrelated.

In a comment you've said:

i call a method which removes then

If you're removing the entry for the loop, you can do that via a list iterator's remove method:

for (var it = routingTable.listIterator(); it.hasNext; ) {
    var entry = it.next();
    if (/*...some condition...*/) {
        it.remove(); // Removes the current entry
    }
}

(There are also add and set operations.)

Sign up to request clarification or add additional context in comments.

3 Comments

I'm not quite sure what do you mean with "You can't (except ...).". Did you forget to complete the sentence or can't you change anything of the List within the synchronized statement?
@Stefanxyz - The sentence is complete, list iterators have some modification methods you can safely use during iteration. To use them, though, you have to explicitly use listIterator and a standard for or while or similar loop, you can't use the enhanced for loop (since it hides the iterator from you). See also the additions I've made to the answer: You're conflating two things that are largely unrelated (synchronization and concurrent modification).
Thanks for the answer. I realized this already thanks to Gabriele Mariotti and tevemadar. I reworked the structure to an normal ´for´ loop which makes it then possible (reducing the index by list changes)
1

ConcurrentModificationException is not necessarily 'concurrent' in the sense of threading, it can be 'concurrent' in the sense of that you shall not directly modify a collection at the same time when you are iterating over it.

It is in the docs too, for a long time (excerpt from Java7: https://docs.oracle.com/javase/7/docs/api/java/util/ConcurrentModificationException.html)

Note that this exception does not always indicate that an object has been concurrently modified by a different thread. If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.

And for(x:y) uses an iterator, which can easily end up being a 'fail-fast' one.

Comments

0

Starting from your comment in the original question, you are removing items in the routingTable while you are iterating.

You can't do it (also if the routingTable is synchronized)

for (RoutingTableEntry entry : routingTable) { // throws it here
     // routingTable.remove(....);
}

1 Comment

thanks i actually thought it was concurrent (@tevemadar mentioned it) and it makes now sense.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.