当一个HashMap实例被多个Thread读取和写入时,我一直在尝试重现(并解决)一个ConcurrentModificationException

免责声明:我知道HashMap不是线程安全的。

在以下代码中:

import java.util.*;

public class MyClass {

    public static void main(String args[]) throws Exception {
        java.util.Map<String, Integer> oops = new java.util.HashMap<>();
        oops.put("1", 1);
        oops.put("2", 2);
        oops.put("3", 3);

        Runnable read = () -> {
            System.out.println("Entered read thread");

            /*
             * ConcurrentModificationException possibly occurs
             *
            for (int i = 0; i < 100; i++) {
                List<Integer> numbers = new ArrayList<>();
                numbers.addAll(oops.values());
                System.out.println("Size " + numbers.size());
            }
            */

            for (int i = 0; i < 100; i++) {
                List<Integer> numbers = new ArrayList<>();
                numbers.addAll(oops.values()
                        .stream()
                        .collect(java.util.stream.Collectors.toList()));
                System.out.println("Size " + numbers.size());
            }
        };

        Runnable write = () -> {
            System.out.println("Entered write thread");
            for (int i = 0; i < 100; i++) {
                System.out.println("Put " + i);
                oops.put(Integer.toString(i), i);
            }
        };

        Thread writeThread = new Thread(write, "write-thread");
        Thread readThread = new Thread(read, "read-thread");

        readThread.start();
        writeThread.start();

        readThread.join();
        writeThread.join();
    }
}

基本上,我创建了两个线程:一个继续将元素放入HashMap ,另一个是在HashMap.values()上迭代。

read线程中,如果我使用numbers.addAll(oops.values()) ,则随机发生ConcurrentModificationException 虽然线条按预期随机打印。

但是,如果我切换到numbers.addAll(oops.values().stream().. ,我没有得到任何错误。但是,我观察到一个奇怪的现象。 read线程的所有行都打印在行后面write线程打印。

我的问题是, Collection.stream()有不同的内部同步?

更新

使用JDoodle https://www.jdoodle.com/a/IYy ,似乎在JDK9和JDK10上,我将按预期获得ConcurrentModificationException

谢谢!

#1楼 票数:1

我能够在Java 8上使用流获取ConcurrentModificationException ,但代码中有一些更改:增加的迭代次数和添加的元素数量,以便在100到10000的单独线程中映射。还添加了CyclicBarrier以便读取器和写入器线程中的循环或多或少同时开始。 我还检查了Hashmap.values()的spliterator的源代码,如果对map进行了一些修改,它会抛出ConcurrentModificationException

if (m.modCount != mc) //modCount is number of modifications mc is expected modifications count which is stored before trying to fetch next element
                throw new ConcurrentModificationException();

#2楼 票数:1 已采纳

你所看到的绝对偶然; 请记住内部System.out.println执行synchronzied ; 因此可能会以某种方式使结果看起来像按顺序出现。

我没有深入研究你的代码 - 因为分析为什么HashMap ,它不是线程安全的,是错过表现的,这很可能是徒劳的; 如您所知,它被记录为非线程安全的。

关于那个ConcurrentModificationException ,该文档是特定的,它将尝试最好的赔率抛出; 所以它或者java-8在这一点上都比较弱,或者这也是偶然的。

#3楼 票数:0

我快速查看了Java 8的源代码,它确实抛出了ConcurrentModificationException HashMapvalues()方法返回AbstractCollection的子类,其spliterator()方法返回一个ValueSpliterator ,它抛出ConcurrentModificationException

有关信息, Collection.stream()使用spliterator遍历或分区源的元素。

  ask by Genzer translate from so

未解决问题?本站智能推荐:

5回复

Collection.stream()的实现

我已经在 J​​DK 1.8 上工作了几天,在那里我遇到了一些与此类似的代码: 现在,对于使用流 ( java.util.stream ) 的人来说可能看起来简单而干净,但我找不到实现java.util.Collection.stream()方法的实际类。 当我说list.stream()时,我有以
3回复

Stream.parallel()是否使用新线程?

所以,我试图了解Java 8中引入的Stream API。我正在尝试创建一个可以在单独的线程上运行的流(仅用于教育目的) 结果不是我所期望的......(我原本期待崩溃,因为我关闭了输入流,而另一个线程正在读取它)。 注意.parallel()方法调用。 相反,代码似乎以顺序方式执行而没
1回复

是否可以仅同步Java流中的终端方法调用?

让我们考虑可以从不同线程访问的集合。 我正在寻找一种线程安全的方式来使用Java流来操作集合。 由于所有中间流操作都是惰性的,因此只能以终端方法执行实际作业。 而且我只能同步终端方法调用。 因此,代码是否是线程安全的:
1回复

从多个线程处理java.util.stream.Stream

Java中是否有一种方法可以并行产生到Stream并从中消耗另一个Thread? 尚未找到任何多线程保证。
1回复

Java8Stream修改mutablevar

我试图理解parallelStreams是如何工作的。 让我们考虑以下示例: 一个主要方法: 结果将是: 如果我将其更改为parallelStream() ,结果可能是这样的: 是否可能会附加少于6位的数字? 例如,如果两个Threads在开头的同时读取值John Doe。
3回复

Java8的Collection.parallelStream如何工作?

Collection类在Java SDK 8中带有新方法“ parallelStream ”。 显然,此新方法提供了一种并行使用集合的机制。 但是,我不知道Java如何实现这种并行性。 潜在的机制是什么? 它仅仅是多线程执行吗? 还是加入了fork / join框架(Java SD
1回复

Java8Stream多线程

对于此代码,它是否在多个线程上运行? 如果没有,我该怎么办? 我希望每个m.dosomething()在单独的线程上运行,以加快这项工作。
2回复

Collection.toArray()与Collection.stream().toArray()

考虑以下代码: 在我看来,使用流要简单得多。 因此,我测试了每个的速度。 结果是使用流大约慢了四倍。 在我的机器上,816 毫秒(流)与 187 毫秒(无流)。 我还尝试切换时序语句(在 myArray1 之前的 myArray2),这对结果没有太大影响。 为什么这么慢? 创建Stream计算量如此