【问题标题】:non-interference requirement on Java 8 streamsJava 8 流的非干扰要求
【发布时间】:2017-06-01 11:13:57
【问题描述】:

我是 Java 8 的初学者。

无干扰对于保持一致的 Java 流行为很重要。 想象一下,我们正在处理大量数据,并且在此过程中 源变了。结果将是不可预测的。这是 不考虑流并行的处理模式或 顺序。

源可以修改,直到语句终端操作为 调用。除此之外,源不应该被修改,直到流 执行完成。所以处理流中的并发修改 源对于获得一致的流性能至关重要。

以上引文摘自here

有人可以做一些简单的例子来解释为什么改变流源会产生如此大的问题吗?

【问题讨论】:

    标签: java java-8 java-stream


    【解决方案1】:

    预言机example 在这里是不言自明的。第一个是这样的:

    List<String> l = new ArrayList<>(Arrays.asList("one", "two"));
     Stream<String> sl = l.stream();
     l.add("three");
     String s = l.collect(Collectors.joining(" "));
    

    如果你改变l,在之前再添加一个元素你调用终端操作(Collectors.joining)你没问题;但请注意,Stream 由三个元素组成,而不是两个;在您通过l.stream() 创建流时。

    另一方面,这样做:

      List<String> list = new ArrayList<>();
      list.add("test");
      list.forEach(x -> list.add(x));
    

    将抛出ConcurrentModificationException,因为您无法更改源。

    现在假设您有一个可以处理并发添加的底层源:

    ConcurrentHashMap<String, Integer> cMap = new ConcurrentHashMap<>();
    cMap.put("one", 1);
    cMap.forEach((key, value) -> cMap.put(key + key, value + value));
    System.out.println(cMap);
    

    这里的输出应该是什么?当我运行它时:

     {oneoneoneoneoneoneoneone=8, one=1, oneone=2, oneoneoneone=4}
    

    把key改成zxcMap.put("zx", 1)),现在的结果是:

    {zxzx=2, zx=1}
    

    结果不一致。

    【讨论】:

    • 我只是想到了list.forEach(x -&gt; list.add(x)); 的歧义,如果它没有抛出ConcurrentModificationException,但通过与ConcurrentHashMap 进行比较来证明它是一个好主意……
    • 在没有其他线程进行更新的情况下,CHM forEach 不会在容量增加时注意到新状态。您可以尝试ConcurrentHashMap&lt;Integer, Integer&gt; cMap = new ConcurrentHashMap&lt;&gt;(1000, 1f); cMap.put(1, 1); cMap.forEach((key, value) -&gt; cMap.put(key + 1, 0)); 并使用初始容量。当容量增加时,您会看到迭代停止(四舍五入到 2 的下一次幂)。
    • 如果要超过 65535 次迭代,就得把 key 适配成 CHM 的spread 函数的效果,例如: ConcurrentHashMap&lt;Integer, Integer&gt; cMap = new ConcurrentHashMap&lt;&gt;(200_000_000, 1f); cMap.put(1, 1); cMap.forEach((key, value) -&gt; cMap.put(++value^(value&gt;&gt;&gt;16), value)); 在我的机器上运行了几分钟,已经消耗了几个 GB,不知道它是否或何时会退出......
    • @Didier L:the specification 表示这相当于循环for ((Map.Entry&lt;K, V&gt; entry : map.entrySet()) action.accept(entry.getKey(), entry.getValue());,这意味着行为应该as weakly consistent as the entryset iterator
    • @Eugene:好吧,你也可能在函数之间产生干扰,但在 Stream API 中讨论了这一点,通常不鼓励“有状态的行为参数”和“行为参数的副作用”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-15
    • 1970-01-01
    • 1970-01-01
    • 2013-05-09
    相关资源
    最近更新 更多