Java 1.8 Parallel-Streams 并行流

更新时间 2023-07-25 17:47:55

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 1v1 提问 / 学习打卡 / 每月赠书活动

目前, 星球 内第2个项目《仿小红书(微服务架构)》正在更新中。第1个项目:全栈前后端分离博客项目2期已经完结,演示地址:http://116.62.199.48/。采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 288 小节,累计 45w+ 字,讲解图:2012 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 1600+ 小伙伴加入,欢迎点击围观

1. 并行流是什么?

Java 8 中引入了 Stream 流新特性,它用于更加简洁、易读的方式处理数据。并行流就是 Stream 的一个分支,它利用多核处理器的优势,可以实现真正的多线程环境下的并行执行

并行流的主要目标是利用多核处理器,以提高大数据集的处理速度。核心思想是,将要处理的数据分割成多个部分,然后并行处理这些部分,最后合并结果。

2. 如何创建并行流?

创建并行流非常简单。你可以在任何 Stream 对象上调用 parallel() 方法,将其转换为并行流。例如:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); //创建一个数字列表

int sum = numbers.stream()
        .parallel() //转换为并行流
        .mapToInt(i -> i * i) //计算每个元素的平方
        .sum(); //对所有平方值求和

System.out.println("The sum of squares is " + sum);

在上面的代码中,我们首先将 numbers 转换为一个 Stream,然后调用 parallel() 方法将其转换为并行流。我们对每个元素求平方,然后对所有平方值求和。

3. 什么时候应该使用并行流?

并行流可以大大提高处理大数据集的速度,但并不总是更快。并行处理带有额外的开销,比如线程切换和额外的内存消耗。因此,只有在数据集足够大且单个元素的处理时间足够长的情况下,使用并行流才能实现性能的提升。

举个例子,假设我们要在一个含有1000万个元素的列表中查找一个元素,使用并行流肯定比顺序流更快。因为在并行流中,数据被分割成多个部分,每个部分在不同的线程中并行处理。然而,如果列表只有100个元素,那么并行流可能并不会比顺序流快。

4. 并行流的注意事项

虽然并行流在处理大数据集时具有明显的优势,但是在使用它时也需要注意一些问题。

  • 线程安全:如果你的函数(例如在 mapreduce 中使用的函数)不是线程安全的,那么并行流可能会导致问题。你应该确保你的函数没有任何副作用,并且能够安全地在多个线程之间共享。
  • 顺序:并行流处理元素的顺序不是固定的,因为元素的处理是在多个线程中并行进行的。如果你关心处理元素的顺序,那么可能不应该使用并行流。
  • 资源限制:如果你的任务需要大量的计算资源(例如CPU或内存),那么在并行流中执行这些任务可能会导致资源耗尽。在这种情况下,你可能需要优化你的任务,或者限制并行流的并发级别。
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10000000; i++) {
    list.add(i);
}

long start = System.currentTimeMillis();
list.stream().forEach(e -> {});
long end = System.currentTimeMillis();

System.out.println("Sequential Stream Time Taken?= " + (end - start) 
                     + "\n");

start = System.currentTimeMillis();
list.parallelStream().forEach(e -> {});
end = System.currentTimeMillis();

System.out.println("Parallel Stream Time Taken?= " + (end - start) + "\n");

上面的代码创建了一个有 10000000 个元素的列表。然后我们用普通流和并行流分别处理这个列表,并比较了他们的处理时间。

5. 结论

在大数据处理方面,Java 8 的并行流是一个非常强大的工具。然而,并行流并不是一个普适的解决方案,我们需要在适当的情况下才使用它。当你打算使用并行流时,一定要确保你的代码是线程安全的,你的任务能够被有效地拆分和合并,且你的系统有足够的资源来处理并行任务。

并行流提供了一种简单的方法来利用多核处理器,而无需我们深入到线程管理和同步问题中。然而,这并不意味着我们可以忽视这些问题。对于那些无法被并行流有效处理的任务,我们可能需要考虑使用其他的并行处理工具,如 ExecutorService 或 ForkJoinPool。

记住,每一种工具都有它的优势和限制。理解这些优势和限制,才能更有效地使用这些工具。并行流就是这样的一种工具。希望这篇教程可以帮助你更好地理解并行流,并能够在适当的时候使用它。