Collection.stream()是否具有内部同步?

时间:2018-10-12 14:45:58

标签: java multithreading java-stream

当多个ConcurrentModificationException读写HashMap的实例时,我一直试图重现(和解决)Thread

免责声明:我知道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是否具有内部同步功能?

更新

使用JDoodle https://www.jdoodle.com/a/IYy,看来在JDK9和JDK10上,我会按预期得到Collection.stream()

谢谢!

3 个答案:

答案 0 :(得分:1)

我能够在Java 8上使用流获得ConcurrentModificationException,但代码有所变化:迭代次数增加,要映射到单独线程中的元素数量也从100增加到10000。并且还添加了{{ 1}},以使读写器线程中的循环或多或少地同时开始。我还检查了CyclicBarrier分离器的源代码,如果对地图做了一些修改,它将抛出Hashmap.values()

ConcurrentModificationException

答案 1 :(得分:1)

您所看到的完全是偶然;请记住,内部System.out.println会执行synchronzied;因此可能是 使结果看起来像按顺序显示。

我还没有深入研究您的代码-因为分析为什么HashMap(不是线程安全的)为什么会错过行为,这很可能是徒劳的;如您所知,它被证明是非线程安全的。

关于该ConcurrentModificationException的文档特别指出,它将尝试尽最大可能抛出该错误;所以在这一点上可能是Java-8较弱,或者又是偶然。

答案 2 :(得分:0)

我快速查看了Java 8的源代码,它确实抛出了--userid的{​​{1}}方法返回ConcurrentModificationException的子类,该子类的HashMap方法返回一个values()并抛出AbstractCollection

有关信息,spliterator()使用分隔符来遍历或划分源元素。