【问题标题】:Is it safe to nest parallel streams?嵌套并行流是否安全?
【发布时间】:2020-07-15 20:29:30
【问题描述】:

我在 java 8 生命周期的早期就已经看到,嵌套并行流会导致死锁,我相信这个问题已经得到解决。他们还有其他可能出现的问题吗?我知道嵌套并行流不会带来那么高的性能,但它似乎仍然可以将我的速度提高大约 50%(将运行时间从 11 分钟减少到 5.5 分钟)。

当然,如果它导致问题,我宁愿放慢速度,但是搜索嵌套的并行流会返回早期的 1.8 问题,而不是当前的任何问题。

这里有一些简化的示例代码(它是为了使 80k 对象列表中的相对引用更容易跨系统传输。在实际代码中,有更多操作和更详细的异常尝试/捕获)-

LinkSystem(List<Obj>objList,String property){

objList.parallelstream
.filter(obj -> obj.getAttribute.equals(property))
.forEach(f->
 {
 try { Optional<Obj>MysteryObj = objList.parallelStream()
.filter(z->z.getAttribute2.equals(f.getAttribute3)
.findAny();
f.setAttribute4(z.MysteryObj.get().getID()); 
});
 } catch(Exception e){e.printStackTrace();}
}

【问题讨论】:

  • 您是在问死锁是一般情况下发生还是仅在指定代码中发生?
  • 好吧,不是死锁 - 更多其他问题。我相信僵局问题已经解决。如果还可以的话,我想我现在应该已经看到了?如果给定代码可能导致并发问题、竞争条件、无用处理,则更多。 (我对嵌套并行流也很好奇,但我目前关心的是这个特定的代码)。
  • 直觉上我没有发现问题。看起来你正在做只读的东西。如果您想自己解决问题,您可能需要研究并行流的实际工作方式。死锁或竞争条件不是像量子物理学那样随机发生的事情。有原因,例如错误的同步。您可以查看this 答案,以更深入地了解并行流的作用并从那里继续。
  • 关键词是时间复杂度。您的嵌套迭代具有 二次 时间复杂度,并且将其除以恒定数量的 CPU 内核(在最佳情况下)不会改变这一点。首先创建从属性 2 到 obj 的映射并在第二次迭代中使用它具有 线性 时间复杂度,并且对于大型数据集运行速度明显更快,即使是顺序流也是如此。我猜,几秒钟而不是你的五分钟……

标签: java java-8 parallel-processing java-stream


【解决方案1】:

据我所知,Java 8 在像您这样的直接嵌套用法中从未遇到过死锁问题。

但是你还是用了错误的方法。嵌套两次迭代会导致 二次时间复杂度,并将其除以恒定数量的 CPU 内核(在最佳情况下)不会改变这一点。

相反,首先从attribute2 值到对应的Obj 创建一个Map,然后在第二次迭代中使用这个映射。连续迭代两次仍然具有线性时间复杂度,并且对于大型数据集运行速度会显着加快,即使是顺序流也是如此。

Map<TypeOfYourAttribute,Obj> attr2Lookup = objList.stream()
    .filter(obj -> obj.getAttribute().equals(property))
    .collect(Collectors.toMap(Obj::getAttribute2, Function.identity()));

objList.stream()
//  .filter(obj -> obj.getAttribute().equals(property))
    .forEach(f -> {
        Obj z = attr2Lookup.get(f.getAttribute3());
        if(z != null) {
            f.setAttribute4(z.getID());
        }
    });

请注意,在第一次迭代中未通过过滤器的对象不会出现在地图中,因此处理来自attr2Lookup.get(…)null 响应就足够了,第二次迭代不需要过滤器。这里有一个小的优化机会。如果预计大多数对象将通过过滤器,则在第二次迭代中省略filter 步骤。但是如果大多数对象都被过滤器拒绝了,那么在这里进行过滤会更有效,因为字符串比较比哈希查找更便宜。

如果没有第二步中的filter,您也可以直接在List 上使用forEach,省略流开销,但使用流打开了在此处使用并行流的机会。

我认为,您可以尝试这些机会,将第一步变为并行永远不会有好处,但第二步可能会有。无论哪种情况,我都希望每种组合的性能都优于 80k 元素的嵌套迭代方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多