我使用Java 8的流和并行流编写了一段代码,实现了相同的功能,并使用自定义收集器执行聚合函数。
当我使用htop
查看CPU使用情况时,无论是“流”还是“并行流”版本,所有CPU核心都被使用。因此,使用list.stream()
时,它似乎也使用了所有的CPU。那么,在多核使用方面,parallelStream()
和stream()
之间有什么精确的区别呢?
我使用Java 8的流和并行流编写了一段代码,实现了相同的功能,并使用自定义收集器执行聚合函数。
当我使用htop
查看CPU使用情况时,无论是“流”还是“并行流”版本,所有CPU核心都被使用。因此,使用list.stream()
时,它似乎也使用了所有的CPU。那么,在多核使用方面,parallelStream()
和stream()
之间有什么精确的区别呢?
考虑以下程序:
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
是同步的。因此,实际上只有一个数字可以同时被写入,尽管使用了多个线程。
延续 @Hoopje 的回答:
在使用 parallelStream()
之前,请阅读以下内容:
n
线程的 ExecutionService 提供比 parallel streams 更好的性能。你也可以阅读: Java Parallel Streams Are Bad for Your Health! | JRebel by Perforce
list.stream()
在发出终端操作的同一线程中按顺序运行,这是100%的事实。然而,我们无法解释为什么你观察到了所有的CPU利用率,因为我们看不到你的代码。 - Tagir Valeev