Java.Util.Concurrentmodificationexception Not Thrown When Expected

java.util.ConcurrentModificationException not thrown when expected

The iterator returned from ArrayList.iterator() in the implementation we're apparently both using only checks for structural modification in calls to next(), not in calls to hasNext(). The latter just looks like this (under Java 8):

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

So in your second case, the iterator "knows" that it's returned two elements, and that the list only has two elements... so hasNext() just returns false, and we never end up calling next() the third time.

I would view this as an implementation detail - basically the checking not being as strict as it could be. It would be entirely reasonable for hasNext() to perform a check and throw an exception in this case too.

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
}
}

It does not throw exception ConcurrentModificationException

The remove(int) method on List removes the element at the specified position. Before you start your loop, your list looks like this:

[1, 2]

Then you start an iterator on the list:

[1, 2]
^

Your for loop then removes the element at position 1, which is the number 2:

[1]
^

The iterator, on the next implied hasNext() call, returns false, and the loop terminates.

You will get a ConcurrentModificationException if you add more elements to the list. Then the implicit next() will throw.

As a note, from the Javadoc for ArrayList from the JCF:

Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

This is probably actually a bug in the Oracle ArrayList iterator implementation; hasNext() does not check for modification:

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

Collection throws or doesn't throw ConcurrentModificationException based on the contents of the Collection

Short answer

Because the fail-fast behavior of an iterator isn't guaranteed.

Long answer

You're getting this exception because you cannot manipulate a collection while iterating over it, except through the iterator.

Bad:

// we're using iterator
for (Iterator<String> i = c.iterator(); i.hasNext();) {
// here, the collection will check it hasn't been modified (in effort to fail fast)
String s = i.next();
if(s.equals("lalala")) {
// s is removed from the collection and the collection will take note it was modified
c.remove(s);
}
}

Good:

// we're using iterator
for (Iterator<String> i = c.iterator(); i.hasNext();) {
// here, the collection will check it hasn't been modified (in effort to fail fast)
String s = i.next();
if(s.equals("lalala")) {
// s is removed from the collection through iterator, so the iterator knows the collection changed and can resume the iteration
i.remove();
}
}

Now to the "why": In the code above, notice how the modification check is performed - the removal marks the collection as modified, and next iteration checks for any modifications and fails if it detects the collection changed. Another important thing is that ArrayList (not sure about other collections) does not check for modification in hasNext().

Therefore, two strange things may happen:

  • If you remove the last element while iterating, nothing will be thrown

    • That's because there's no "next" element, so the iteration ends before reaching the modification-checking code
  • If you remove the second-to-last element, ArrayList.hasNext() will actually also return false, because the iterator's current index is now pointing at the last element (former second-to-last).

    • So even in this case, there's no "next" element after the removal

Note that this all is in line with ArrayList's documentation:

Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

Edited to add:

This question provides some information on why the concurrent modification check is not performed in hasNext() and is only performed in next().

How to avoid java.util.ConcurrentModificationException when iterating through and removing elements from an ArrayList

Two options:

  • Create a list of values you wish to remove, adding to that list within the loop, then call originalList.removeAll(valuesToRemove) at the end
  • Use the remove() method on the iterator itself. Note that this means you can't use the enhanced for loop.

As an example of the second option, removing any strings with a length greater than 5 from a list:

List<String> list = new ArrayList<String>();
...
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
String value = iterator.next();
if (value.length() > 5) {
iterator.remove();
}
}

Why doesn't this code throw a ConcurrentModificationException?

As a general rule, ConcurrentModificationExceptions are thrown when the modification is detected, not caused. If you never access the iterator after the modification, it won't throw an exception. This minute detail makes ConcurrentModificationExceptions rather unreliable for detecting misuse of data structures, unfortunately, as they only are thrown after the damage has been done.

This scenario doesn't throw a ConcurrentModificationException because next() doesn't get called on the created iterator after the modification.

For-each loops are really iterators, so your code actually looks like this:

List<String> strings = new ArrayList<>(Arrays.asList("A", "B", "C"));
Iterator<String> iter = strings.iterator();
while(iter.hasNext()){
String string = iter.next();
if ("B".equals(string))
strings.remove("B");
}
System.out.println(strings);

Consider your code running on the list you provided. The iterations look like:

  1. hasNext() returns true, enter loop, -> iter moves to index 0, string = "A", not removed
  2. hasNext() returns true, continue loop -> iter moves to index 1, string = "B", removed. strings now has length 2.
  3. hasNext() returns false (iter is currently at the last index, no more indices to go), exit loop.

Thus, as ConcurrentModificationExceptions are thrown when a call to next() detects a that a modification has been made, this scenario narrowly avoids such an exception.

For your other two results, we do get exceptions. For "A", "B", "C", "D", after removing "B" we are still in the loop, and next() detects the ConcurrentModificationException, whereas for "A", "B" I'd imagine it's some kind of ArrayIndexOutOfBounds that's being caught and re-thrown as a ConcurrentModificationException

Some times java.util.ConcurrentModificationException thrown

This is from the JavaDoc for ConcurrentModificationException:

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.

In your last loop, you sometimes do

user.getSkillsList().add(skillObj);

while iterating using user.getSkillsList().iterator().

Why this code does not trigger ConcurrentModificationException?

Your code does (and can) produce a ConcurrentModificationException. You are not catching it to print it.

With the below, we can see that it indeed throws many ConcurrentModificationException.

try {
for (String val : list) {
try {
Thread.sleep(25);
} catch (Exception e) {
}
}
} catch (Exception e) {
e.printStackTrace();
}

NOTE: From the javadoc,

Note that the fail-fast behavior of an iterator cannot be guaranteed
as it is, generally speaking, impossible to make any hard guarantees in the
presence of unsynchronized concurrent modification. Fail-fast iterators
throw {@code ConcurrentModificationException} on a best-effort basis.
Therefore, it would be wrong to write a program that depended on this
exception for its correctness: the fail-fast behavior of iterators
should be used only to detect bugs

Also see: java.util.ConcurrentModificationException not thrown when expected


Or, you could get the returned Future and collect them in a list. By doing that, you will get the exception when calling get on (atleast) one of the futures.



Related Topics



Leave a reply



Submit