Java 8流和并行流的区别

41

我使用Java 8的流和并行流编写了一段代码,实现了相同的功能,并使用自定义收集器执行聚合函数。 当我使用htop查看CPU使用情况时,无论是“流”还是“并行流”版本,所有CPU核心都被使用。因此,使用list.stream()时,它似乎也使用了所有的CPU。那么,在多核使用方面,parallelStream()stream()之间有什么精确的区别呢?


7
非并行流只使用一个线程来处理其管道。这是一个不可改变的事实。除非您在流处理中进行了一些显式的多线程处理,那么任何给定的终端操作都将一次在单个核心上执行。如果您提到 htop 显示所有核心的某些利用率,那可能只是由于同一线程从一个核心迁移到另一个核心(而不是固定在单个核心上)。 - Marko Topolnik
2
最好提供你程序的代码,这样我们才能重现你的效果。正如马尔科所说,list.stream() 在发出终端操作的同一线程中按顺序运行,这是100%的事实。然而,我们无法解释为什么你观察到了所有的CPU利用率,因为我们看不到你的代码。 - Tagir Valeev
请在此处找到代码 - https://github.com/yogirjoshi/monitortools/blob/master/src/main/java/rithm/driver/Hypothesis2.java - Yogi Joshi
我认为这个答案可能会对你有所帮助:https://dev59.com/7WAg5IYBdhLWcg3wyNhv - Harry_T
2个回答

60

考虑以下程序:

import java.util.ArrayList;
import java.util.List;

public class Foo {
    public static void main(String... args) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            list.add(i);
        }
        list.stream().forEach(System.out::println);
    }
}

你会注意到这个程序将按照列表中的顺序连续输出从0到999的数字。如果我们将stream()更改为parallelStream(),那么情况就不再是这样了(至少在我的电脑上):所有数字都被写入,但是顺序不同。因此,显然,parallelStream()确实使用了多个线程。

htop之所以存在是因为即使单线程应用程序也会被大多数现代操作系统分割成多个核心(同一线程的部分可能在多个核心上运行,但当然不能同时)。因此,如果您看到一个进程使用了多个核心,这并不一定意味着该程序使用了多个线程。

此外,使用多个线程也不一定能提高性能。同步的开销可能会抵消使用多个线程所带来的收益。对于简单的测试场景,通常情况下都是如此。例如,在上面的例子中,System.out是同步的。因此,实际上只有一个数字可以同时被写入,尽管使用了多个线程。


3

延续 @Hoopje 的回答:

在使用 parallelStream() 之前,请阅读以下内容:

  1. 它是多线程的。仅仅为了获得并行性而写 parallelStream() 在Java中几乎总是不好的选择。虽然有一些情况下它可能有效,但并不总是这样。在采取多线程方案之前,你需要思考很多其他方法来实现并行处理。
  2. 它使用JVM默认线程池。 因此,如果你执行任何阻塞操作(例如网络调用),整个Java应用程序都可能会卡住。 这是最大的问题。还有其他任务分配方面的问题。一个简单的具有 n 线程的 ExecutionService 提供比 parallel streams 更好的性能。

你也可以阅读: Java Parallel Streams Are Bad for Your Health! | JRebel by Perforce


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接