Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ConcurrentModificationException only in Java 1.8.0_45

I've got two question about this code:

import java.util.*;

public class TestClass {

    private static List<String> list;   
    public static void main(String[] argv) {

        list = generateStringList(new Random(), "qwertyuioasdfghjklzxcvbnmPOIUYTREWQLKJHGFDSAMNBVCXZ1232456789", 50, 1000);

//      Collections.sort(list, new Comparator<String>() {
//          public int compare(String f1, String f2) {
//              return -f1.compareTo(f2);
//          }
//      });

        for (int i = 0; i < 500; i++) {
            new MyThread(i).start();
         }

    }

    private static class MyThread extends Thread  {
        int id;
        MyThread(int id) { this.id = id; }
        public void run() {

            Collections.sort(list, new Comparator<String>() {
                public int compare(String f1, String f2) {
                    return -f1.compareTo(f2);
                }
            });

            for (Iterator it = list.iterator(); it.hasNext();) {
                String s = (String) it.next();
                try {
                    Thread.sleep(10 + (int)(Math.random()*100));
                }catch (Exception e) { e.printStackTrace(); }

                System.out.println(id+" -> "+s);
            }           
        }       
    }

    public static List<String> generateStringList(Random rng, String characters, int length, int size)
    {
        List<String> list = new ArrayList<String>();
        for (int j = 0; j < size; j++) {
            char[] text = new char[length];
            for (int i = 0; i < length; i++)
            {
                text[i] = characters.charAt(rng.nextInt(characters.length()));
            }
            list.add(new String(text));
        }
        return list;
    }
}

Running this code on java 1.8.0_45 i got java.util.ConcurrentModificationException.

1) Why I got the exception also if I decomment the sort before the thread.start?

2) Why I only got the exception on java 1.8.0_45? On 1.6.0_45, 1.7.0_79, 1.8.0_5 it works fine.

like image 818
Alvins Avatar asked Dec 04 '22 02:12

Alvins


2 Answers

@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.

like image 108
eis Avatar answered Dec 06 '22 20:12

eis


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().

like image 20
nbokmans Avatar answered Dec 06 '22 19:12

nbokmans