Why Is a Concurrentmodificationexception Thrown and How to Debug It

Why is a ConcurrentModificationException thrown and how to debug it

This is not a synchronization problem. This will occur if the underlying collection that is being iterated over is modified by anything other than the Iterator itself.

Iterator it = map.entrySet().iterator();
while (it.hasNext()) {
Entry item = it.next();
map.remove(item.getKey());
}

This will throw a ConcurrentModificationException when the it.hasNext() is called the second time.

The correct approach would be

Iterator it = map.entrySet().iterator();
while (it.hasNext()) {
Entry item = it.next();
it.remove();
}

Assuming this iterator supports the remove() operation.

ConcurrentModificationException thrown when running in debugger

No, this class is almost certainly not safe to use in production.
Debuggers don't normally change the content of a collection. And that's what happened here. The content of countsList was changed, while you were iterating over it.

Try to find the source of the modification with the debugger or with log. Maybe you want to set breakpoints in certain methods of ArrayDequeue or override it and place some log in it.

How to debug ConcurrentModificationException?

It may have nothing to do with the synchronization block. ConcurrentModificationExceptions often occur when you're modifying a collection while you are iterating over its elements.

List<String> messages = ...;
for (String message : messages) {
// Prone to ConcurrentModificationException
messages.add("A COMPLETELY NEW MESSAGE");
}

Getting Concurrent Modification Exception

You can change slightly your for loop to avoid this exception:

Pair<UUID, UUID> pairToRemove = null;
for (Pair<UUID, UUID> pair : timeStopList) {
if (pair.getA() == playerUUID) {
pairToRemove = pair; // assign the found pair to pairToRemove variable
break;
}
}
if (pairToRemove != null) {
cancelTimeStop(pairToRemove.getB());
}

UPDATE

P.S. I made the assumption that once a pair.getA() == playerUUID is found there is no need to continue the loop. If there are multiple chances that this if statement evaluates to true then you can adjust the code to:

List<Pair<UUID, UUID>> pairToRemoveList = new ArrayList();
for (Pair<UUID, UUID> pair : timeStopList) {
if (pair.getA() == playerUUID) {
pairToRemoveList.add(pair);
}
}
if (!pairToRemoveList.isEmpty()) {
for (Pair<UUID, UUID> pair : pairToRemoveList) {
cancelTimeStop(pair.getB());
}
}

List throws ConcurrentModificationException but set does not throws ConcurrentModificationException?

This is a difference in the implementation: the iterator returned by the array list detects concurrent modifications even when it is positioned at the end, because it checks the length; iterators of the HashSet, TreeSet and LinkedList, on the other hand, do not detect this condition, because they check for being positioned at the end before checking for concurrent modification. The documentation allows iterators not to throw on concurrent modifications, so both approaches are valid.

  • Demo for the TreeSet.
  • Demo for the LinkedList.

ConcurrentModificationException logic breakdown

This line removes an element from the priority queue:

    node nextFrequent = nodePQ.poll();

It happens inside a loop iterating over the same priority queue, so counts as a forbidden concurrent modification. This causes your exception.

is there a better way you can think of iterating over a PriorityQueue
to remove items? just a regular for loop w PQ.size() ?

If you want to exhaust the queue in priority order, keep taking out elements until poll() returns null (code is not tested):

    node nextFrequent = nodePQ.poll();
while (nextFrequent != null) {
// Do something with nextFrequent
nextFrequent = nodePQ.poll();
}

The poll method Retrieves and removes the head of this queue, or returns null if this queue is empty.

Documentation link: Queue.poll()

In Java how can this throw a ConcurrentModificationException in a single threaded program?

This snippet will always throw a ConcurrentModificationException.

The rule is the following: You may not modify (add or remove elements from the list) while iterating over it using an Iterator (which happens when you use a for-each loop).

Note however that you are allowed to modify the list through the iterator (because then it is aware of the modification, and can take it into account) using the Iterator.remove or ListIterator.add.

From the docs:

The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException.

The word concurrent here refers to that you're modifying the list during traversal.

For java List concurrent modification exception not thrown if remove method invoked before iteration

Look at ArrayList.Itr.

There're two counters: ArrayList.modCount and ArrayList.Itr.expectedModCount. ArrayList.Itr throws ConcurrentModificationException when modCount != expectedModCount.

This is correct. But if you look at ArrayList.Itr.hasNext you can see:

public boolean hasNext() {
return cursor != size;
}

When you remove one element and you list become empty, then your code does not invoke it.next().

To make your code throw ConcurrentModificationException, you should have not empty list before while loop:

public static void main(String[] args)
{
List<String> l1 = new ArrayList<>();
l1.add("v1");
l1.add("v2");
Iterator<String> it = l1.iterator();
l1.remove(0);
while(it.hasNext()) {
System.out.println(it.next()); // --> ConcurrentModificationException
}
}


Related Topics



Leave a reply



Submit