【问题标题】:Java parallelStream not showing correct result [duplicate]Java parallelStream没有显示正确的结果[重复]
【发布时间】:2018-05-22 23:08:12
【问题描述】:

我正在对迭代集合的 Java aggeragate 操作进行一些性能评估。我正在评估streamparallelStream 的性能。但是我发现parallelStream 的输出大多数时候都是错误的。例如,在下面的代码中,我从parallelStream 得到错误输出的概率超过 80%:

    public class ParallelStreamPerformance {
    static int totaleven = 0;
    public static void main(String[] args) {
        List<Integer> randomList = new ArrayList<>();
        Random rnd = new Random();
        for(int i = 0 ;i < 1000;i++) {
            int r = rnd.nextInt(500000);
            randomList.add(r);
        }

        long s1 = System.currentTimeMillis();

        randomList.stream().filter(e -> e%2 ==0).forEach(e -> count());
        System.out.println("Even: "+totaleven);
        long e1 = System.currentTimeMillis();
        System.out.println(e1 - s1);

        totaleven = 0;
        long s2 = System.currentTimeMillis();

        randomList.parallelStream().filter(e -> e%2 ==0).forEach(e -> count());
        System.out.println("Even: "+totaleven);
        long e2 = System.currentTimeMillis();
        System.out.println(e2 - s2);
    }
    public static void count() {
        totaleven++;
    }
}

我的问题是:我是否以错误的方式使用parallelStream?有什么办法可以保证parallelStream的正确性。 谢谢

【问题讨论】:

  • 您的基准测试方法比一文不值还要糟糕;它会给你答案,但他们会错的。见stackoverflow.com/questions/504103/…
  • 谢谢@BrianGoetz
  • 当你的实际问题是关于得到错误的结果时,你不应该用不相关的基准测试来混淆它。你的代码本可以缩短到十行,而关于你进行“性能评估”的散文也已经过时了。当您的代码无法正常运行时,您无需衡量其性能。

标签: java java-8 java-stream


【解决方案1】:

你可以使用终端操作Stream:count而不是使用forEach来增加一个计数器

例如

totaleven = randomList.stream().filter(e -> e % 2 ==0).count();
totaleven = 0;
totaleven = randomList.parallelStream().filter(e -> e % 2 ==0).count();

totaleven 需要更改为 long 数据类型或应用强制转换。

【讨论】:

    【解决方案2】:

    并行流结果有什么问题?如果它太小,那么您很可能对totaleven++ 有问题,因为它不是线程安全的。使用 AtomicInteger 或任何其他线程安全的解决方案。

    【讨论】:

      【解决方案3】:

      我认为您的代码与 count() 方法存在问题。因为parallelStream 将尝试同时执行任务。此方法应为 synchronized 或者您可以将 totaleven 设为 Atomtic Integer。希望对您有所帮助。

      【讨论】:

      • 是的,使用同步方法解决问题。谢谢
      • 所以现在您的代码正确,但并行代码不太可能比顺序代码更快(这是使用并行性的唯一原因),因为线程将花费所有时间等待锁定。您想要做的是使用count()reduce(),而不是增加共享的可变计数器。这更容易正确(不需要同步)并且对并行更友好。
      • @BrianGoetz:将forEach 添加到 Stream API 可能是一个设计错误……
      • @Holger 我们确实讨论过这个问题,因为任何行为参数不返回任何内容的流操作的唯一目的是产生副作用。但实际上,如果我们这样做了,我们会(并且应该)被笑出房间。那只会把我们的头藏在沙子里。 (然后人们会选择其他一些终端操作并在内部完成他们的副作用,例如 map() 调用,然后收集到一个空的接收器中以模拟 forEach()。)无论好坏,副作用都是Java 的一部分。我们最多只能劝阻他们。
      • @BrianGoetz:也许,一个不太容易找到的名字有帮助,例如看看forEachOrdered 的存在被忽略了多少……
      猜你喜欢
      • 1970-01-01
      • 2017-02-10
      • 1970-01-01
      • 2017-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-02
      • 2015-02-01
      相关资源
      最近更新 更多