【问题标题】:Does Collection.stream() have internal synchronization?Collection.stream() 是否有内部同步?
【发布时间】:2018-10-12 14:45:57
【问题描述】:

HashMap 的一个实例被多个Threads 读写时,我一直在尝试重现(并解决)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

谢谢!

【问题讨论】:

    标签: java multithreading java-stream


    【解决方案1】:

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

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

    关于ConcurrentModificationException,文档明确指出它会尝试最好的机会抛出它;所以要么java-8在这一点上较弱,要么这又是偶然的。

    【讨论】:

      【解决方案2】:

      我能够在 Java 8 上获得带有流的 ConcurrentModificationException,但代码发生了一些变化:增加了迭代次数和添加元素的数量,以便在单独的线程中从 100 映射到 10000。还添加了 CyclicBarrier 所以读取器和写入器线程中的循环或多或少同时启动。我还检查了 Hashmap.values() 的拆分器的源代码,如果对地图进行了一些修改,它会抛出 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();
      

      【讨论】:

        【解决方案3】:

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

        有关信息Collection.stream() 使用拆分器来遍历或划分源的元素。

        【讨论】:

          猜你喜欢
          • 2018-11-03
          • 1970-01-01
          • 2021-12-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-09-06
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多