ConcurrentModificationException仅适用于Java 1.8.045

ConcurrentModificationException仅适用于Java 1.8.045,java,concurrency,Java,Concurrency,关于这个代码,我有两个问题: import java.util.*; public class TestClass { private static List<String> list; public static void main(String[] argv) { list = generateStringList(new Random(), "qwertyuioasdfghjklzxcvbnmPOIUYTREWQLKJHGFDSAMN

关于这个代码,我有两个问题:

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;
    }
}
import java.util.*;
公共类TestClass{
私有静态列表;
公共静态void main(字符串[]argv){
列表=generateStringList(new Random(),“QWERTYUIOASDFGJKLZXCVBNMPOIUYTREWQLKJHGFDSAMNBVCXZ1232456789”,50,1000);
//Collections.sort(list,newcomparator(){
//公共整数比较(字符串f1、字符串f2){
//返回-f1。与(f2)比较;
//          }
//      });
对于(int i=0;i<500;i++){
新的MyThread(i.start();
}
}
私有静态类MyThread扩展线程{
int-id;
MyThread(int-id){this.id=id;}
公开募捐{
Collections.sort(list,newcomparator(){
公共整数比较(字符串f1、字符串f2){
返回-f1。与(f2)比较;
}
});
for(Iterator it=list.Iterator();it.hasNext();){
String s=(String)it.next();
试一试{
sleep(10+(int)(Math.random()*100));
}catch(异常e){e.printStackTrace();}
System.out.println(id+“->”+s);
}           
}       
}
公共静态列表生成器列表(随机rng、字符串、整数长度、整数大小)
{
列表=新的ArrayList();
对于(int j=0;j
在Java1.8.045上运行这段代码,我得到了
java.util.ConcurrentModificationException

1) 如果在thread.start之前取消排序,为什么会出现异常


2) 为什么我只在Java1.8.045上得到了这个异常?在1.6.0_45、1.7.0_79、1.8.0_5上,它可以正常工作。

当不允许修改对象时,已检测到对象并发(即在单独线程中)修改的方法会引发ConcurrentModificationException

出现此异常的原因是,您正在一个单独的线程中修改(排序)集合并对其进行迭代

我引用ConcurrentModificationException javadoc:

例如,通常不允许一个线程在另一个线程迭代集合时修改集合。通常,在这些情况下,迭代的结果是未定义的

在代码中,您将启动500个线程,每个线程对列表进行排序和迭代


在启动线程之前,请尝试对列表进行排序,并从MyThread的#run()中删除对集合#sort的调用。

您会遇到此异常,因为您有单独的线程同时修改和迭代列表

注释掉的排序不会导致问题。CME是由线程内部的排序和迭代引起的。因为您有多个线程排序和迭代,所以您得到了一个CME。这不依赖于Java版本

看起来您的线程不需要修改列表,因此您可以在创建线程的循环之前执行一次排序,然后从线程中删除if。

@nbokmans这就是为什么会出现该异常。然而,这似乎是依赖于版本的。我将解释为什么在java 8.0_45中会出现这种情况,而不是1.6.0_45、1.7.0_79、1.8.0_5

这是因为Collections.sort()在Java8.0\u20中发生了更改。有一篇关于它的深入文章。根据这篇文章,在新版本中,排序如下:

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

publicsvoid排序(Comparator使用Java 8,重新实现了
Collections::sort
方法,以委托给
List::sort
方法。这样,如果给定的实现可能,列表可以实现更高效的排序算法。例如,
ArrayList
可以使用其随机访问属性来实现更高效的排序算法客户端排序算法比无随机访问的
链接列表
更有效

ArrayList::sort
的当前实现显式检查修改,因为该实现是在类中定义的,并且能够访问iternal属性

在Java 8之前,
Collections::sort
方法必须实现实际的排序,并且不能委托。当然,该实现可以访问特定列表的任何内部属性。更通用的排序实现如下:

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]);
    }
}

公共静态无效排序(List,Comparator你能支持它不依赖于Java版本的说法吗?我用code OP使用进行了测试,事实上对于Java 7,似乎没有任何异常。@eis-尝试设置测试,使其持续运行。我怀疑你在运行期间可能只是运气好。@johnmclean我不这么认为-测试似乎是一致的stant,似乎对所有这些都有一个实际的解释。添加了我自己的答案。@eis-有趣-做得好!值得强调的是,以前的版本并不能保证工作,只是添加了检查和抛出已更改的异常。例如,如果有人在一个线程上对集合排序,则没有异常另一个线程上的可见性保证,以及在其他线程发生更改后重新使用该线程可能会导致各种各样的混乱。我知道为什么会出现这种异常。我想问的是,为什么它依赖于java版本,为什么在启动Threadssory之前对列表进行排序,我仍然会得到它。我不确定为什么您的代码在1.8.045之前运行良好。我试过了尽我所能回答。感谢您的解释,有什么建议的修复方法吗?@ronnyfm修复方法不是同时使用多个线程对集合进行排序。