在多线程环境下,Collections.sort方法有时会抛出ConcurrentModificationException异常。但是,列表在结构上没有被修改。

8
    package CollectionsTS;

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.HashSet;
    import java.util.List;

    public class ArrayListTS {
        public static void main(String[] args) {
            HashSet<Integer> hset = new HashSet<Integer>();
            for (int i = 0; i <= 1000; i++) {
                hset.add(i);
            }

            MyRunnable mr = new MyRunnable();
            mr.addElements(hset);

            Thread t1 = new Thread(mr,"t1");
            Thread t2 = new Thread(mr,"t2");
            Thread t3 = new Thread(mr,"t3");

            t1.start(); t2.start(); t3.start();

        }
    }

    class MyRunnable implements Runnable {

        List<Integer> ilist = new ArrayList<Integer>();

        public void addElements(HashSet<Integer> hset) {
            ilist.addAll(hset);
        }

        @Override
        public void run() {
            Collections.sort(ilist);
            if (ilist.size() > 0)
                System.out.println( Thread.currentThread().getName() +" = "+ilist.get(ilist.size() - 1));
            else
                System.out.println("List is empty");
        }
    }

抛出的异常是ConcurrentModificationException,我在想代码没有修改列表(结构上),为什么会出现这个异常。

Exception in thread "t1" t3 = 1000
Exception in thread "t2" java.util.ConcurrentModificationException
    at java.util.ArrayList.sort(Unknown Source)
    at java.util.Collections.sort(Unknown Source)
    at CollectionsTS.MyRunnable.run(ArrayListTS.java:37)
    at java.lang.Thread.run(Unknown Source)
java.util.ConcurrentModificationException
    at java.util.ArrayList.sort(Unknown Source)
    at java.util.Collections.sort(Unknown Source)
    at CollectionsTS.MyRunnable.run(ArrayListTS.java:37)
    at java.lang.Thread.run(Unknown Source)

我有一个方法,它返回列表中的最大值,我不想使用Collections.max(),我想使用collections.sort方法在多线程环境下对列表进行排序。

Collections.sort方法有时会在多线程环境中引发ConcurrentModificationException。列表没有结构性修改。

有人可以帮我吗?


你具体使用的是哪个版本的Java? - Sotirios Delimanolis
我提问是因为我正在查看的 Collections#sort 版本没有调用 ArrayList#sort - Sotirios Delimanolis
2个回答

12
你创建了一个单独的MyRunnable实例,该实例具有一个ArrayList成员变量。然后在3个单独的线程中,你尝试对ArrayList进行排序。调用sort将结构性地修改列表。这就是为什么它会导致ConcurrentModificationException的结果。

好的,但是为什么调用排序方法会在结构上修改列表,而列表的大小保持不变。 - Shiva Garg
2
虽然文档不是非常清楚,但 sort 方法也被认为是一种修改。 基本上除了 set 方法之外改变方法内容的任何方法都被认为是修改。 这里是 sort 方法的 jdk8 源代码。 您可以看到 mod 计数器被递增:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/util/ArrayList.java?av=f#1443 - Brett Okken
@ShivaGarg 这个回答解决了你的问题吗? - Brett Okken

2
这是因为java 8.0_20中的Collections.sort()发生了改变。这里有一篇深入的文章介绍了它。 与旧的Collections.sort相反,这个实现在列表已经排序后修改了集合的modCount,即使结构本身并没有真正改变(元素数量仍然相同)。
因此,即使集合已经排序,它也会进行内部更改,在更改之前不会这样做。这就是为什么现在会出现异常的原因。
实际的解决方法是不要同时使用多个线程对集合进行排序。你不应该这样做。

我已经添加了一个链接到Edouard Kaiser的Medium文章,希望那就是你想要的。 - Philippe Cloutier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接