I\'ve got two question about this code:
import java.util.*;
public class TestClass {
private static List list;
public static void
@nbokmans already nailed the general reason why you get that exception. However, it's true that this seems to be version dependant. I'll fill in why you get that in java 8.0_45 but not 1.6.0_45, 1.7.0_79, 1.8.0_5.
This is due to fact that Collections.sort() was changed in java 8.0_20. There's an in-depth article about it here. In the new version, sort, according to the article, is like this:
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
Like the article explains:
Contrary to the old Collections.sort, this implementation modifies the modCount of the collection (line 7 above) once the list has been sorted, even if the structure itself didn’t really change (still the same number of elements).
So it will do an internal change even if the collection is already sorted, whereas before that change it didn't do that. That's why you're getting an exception now.
The actual fix is to not to sort a collection using multiple threads at the same time. You shouldn't do that.
With Java 8, the Collections::sort
method was reimplemented to delegate to the List::sort
method. This way, a list can implement a more efficient sort algorithm if this is possible for the given implementation. For example, an ArrayList
can use its random access property to implement a more effcient sorting algorithm than a LinkedList
without random access.
The current implementation for ArrayList::sort
explicitly checks for modifications as the implementation is defined within the class and is capable of accessing iternal properties.
Before Java 8, the Collections::sort
method had to implement the actual sorting itself and could not delegate. Of course, the implementation could not access any internal properties of the specific list. The more generic sorting was implemented as follows:
public static <T> void sort(List<T> list, Comparator<? super T> c) {
Object[] a = list.toArray();
Arrays.sort(a, (Comparator)c);
ListIterator i = list.listIterator();
for (int j=0; j<a.length; j++) {
i.next();
i.set(a[j]);
}
}
The implementation first extracts a copy of elements and delegates the sorting to the implementation of Arrays::sort
. This can not cause the observed exception as the sorting is conducted on a non-shared copy of elements. Later, the elements are updated element by element according to the sorted array by using a ListIterator
.
For an ArrayList
, the ArrayList
and its iterator keep track of the number of structural modifications, i.e. modifications that change the size of the list. If those numbers diverge for the iterator and the list, the iterator can know that the list was modified outside of its own iteration. It is however not capable of discovering that the elements of a list were altered as it happens for the Collections::sort
implementation.
The contract of a ArrayList
does however not permit concurrent modifications in its contract. Despite the sorting not failing before Java 8, applying the sorting could lead to incorrect results. Since Java 8, it is however for the first time that this is discovered by the implementation.
A ConcurrentModificationException is thrown by methods that have detected concurrent (i.e. in a separate thread) modification of an object when such modification is not permissible.
The reason you're getting this exception is because you are modifying (sorting) the collection in a separate thread and iterating it.
I quote from the ConcurrentModificationException javadoc:
For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it. In general, the results of the iteration are undefined under these circumstances.
Source
In your code, you are starting 500 threads that each sort and iterate over the list.
Try sorting the list before you start your threads, and remove the call to Collections#sort from MyThread's #run().
You're getting this exception because you have separate threads modifying and iterating the list at the same time.
The commented out sort is not causing the problem. The CME is caused by the sort and the iteration inside the thread. Since you have multiple threads sorting and iterating, you're getting a CME. This is not dependent on the Java version.
It looks like your threads don't need to modify the list, so you can perform the sort once before your loop that creates threads, then remove if from the thread.