Reputation: 31801
I am expecting the code below to throw a ConcurrentModificationException, but instead I am getting an ArrayIndexOutOfBoundsException.
I am aware that a ConcurrentModificationException is not guaranteed as per javadoc included below, but I am attempting to recreate a scenario that leads to ConcurrentModificationException in order to better understand the mechanism of concurrency errors.
From ArrayList javadoc:
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.
public class RaceConditions {
private static final int NUM_THREADS = 1000;
private static final int NUM_ELEMENTS = 10000;
private List<Integer> list = new ArrayList<>();
public static void main(String... args) {
for (int i = 0; i < NUM_THREADS; i++) {
final int fi = i;
new Thread() {
@Override
public void run() {
for (int j = 0; j < NUM_ELEMENTS; j++) {
list.add(0, fi);
}
}
}.start();
}
System.out.println(list.size());
}
}
My question is what would I need to modify in the code above in order to get a guaranteed (or almost guaranteed) ConcurrentModificationException?
Upvotes: 3
Views: 550
Reputation: 1
This should help in generating the concurrentmodificationexception: Access element via iterator and also modifying list directly
List<String> data1 = new ArrayList();
data1.add("A");
data1.add("AB");
data1.add("ABC");
data1.add("ABCD");
Iterator itr = data1.iterator();
while (itr.hasNext()) {
itr.next();
System.out.println(itr.next());
itr.remove();
data1.add("E");
}
Upvotes: 0
Reputation: 48874
Despite the name, a ConcurrentModificationException
does not only occur in multi-threaded applications. Instead it usually indicates a collection was modified while you were iterating or otherwise interacting with the collection. As a RuntimeException
, it generally indicates a programming error. If you don't misuse the collections you're working with, you should never see a CME.
The easiest way to generate one is to call .remove()
from inside a for-each loop. This happens because the Iterator
that the for-each loop is using is keeping track of where in the collection it is (e.g. for an ArrayList
it's tracking the current index). If you modify the collection by calling .remove()
the iterator's state is no longer consistent with the collection's state, and therefore the iterator will fail-fast by raising a CME if it's able to detect the inconsistency.
Here's an example:
ArrayList
containing [1, 2, 3, 4]
.iterator()
to get an Iterator
of this list. Internally the Iterator
will be pointed at index 0
..next()
on the iterator, returning 1
and advancing the internal pointer to index 1
..remove(0)
on the list, to remove the 0th element, 1
, from the list, and shift the remaining elements forward one. Our list is now [2, 3, 4]
..next()
on the iterator. Since it's internal state points to index 1
it would return 3
, skipping 2
, which would be a problem. If possible the iterator will detect this inconsistency and fail, rather than silently continue erroneously.All the collections in java.util
do extra work to detect these inconsistencies and raise CMEs when possible, and most other popular collections (like Guava's) do so as well. But it's not possible to always detect these issues and so they can only promise a best-effort level of error-checking, which is why the Javadoc has to be somewhat vague.
Upvotes: 2
Reputation: 137269
You are misunderstanding this exception. From ConcurrentModificationException
:
This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible.
For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it.
In your example, there is no iteration over the list so this exception can never be thrown.
Upvotes: 1
Reputation: 2117
This will give you always an ConcurrentModificationException
public class App {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Test");
list.add("Test");
list.add("Test");
list.add("Test");
list.add("Test");
for(String item : list) {
list.remove(item);
}
}
}
This exception is thrown when ever you try to change a collection while you currently iterating over it.
Upvotes: 3