只要使用这个功能,程序运行速度瞬间提升,高到离谱!

开发 前端
在之前如果需要处理集合需要先手动分成几部分,然后为每部分创建线程,最后在合适的时候合并,这是手动处理并行集合的方法,在java8中,有了新功能,可以一下开启并行模式。

[[357631]]

本文转载自微信公众号「小明菜市场」,作者小明菜市场 。转载本文请联系小明菜市场公众号。

 前言

在之前如果需要处理集合需要先手动分成几部分,然后为每部分创建线程,最后在合适的时候合并,这是手动处理并行集合的方法,在java8中,有了新功能,可以一下开启并行模式。

并行流

认识开启并行流

并行流是什么?是把一个流内容分成多个数据块,并用不同线程分别处理每个不同数据块的流。例如,有下面一个例子,在List中,需要对List数据进行分别计算,其代码如下所示:

  1. List<Apple> appleList = new ArrayList<>(); // 假装数据是从库里查出来的 
  2.  
  3. for (Apple apple : appleList) { 
  4.     apple.setPrice(5.0 * apple.getWeight() / 1000); 

在这里,时间复杂度为O(list.size),随着list的增加,耗时也在增加。并行流可以解决这个问题,代码如下所示:

appleList.parallelStream().forEach(apple -> apple.setPrice(5.0 * apple.getWeight() / 1000));

这里通过调parallelStream()说明当前流为并行流,然后进行并行执行。并行流内部使用了默认的ForkJoinPool线程池,默认线程数为处理器的核心数。

测试并行流

普通代码如下所示:

  1. public static void main(String[] args) throws InterruptedException { 
  2.     List<Apple> appleList = initAppleList(); 
  3.  
  4.     Date begin = new Date(); 
  5.     for (Apple apple : appleList) { 
  6.         apple.setPrice(5.0 * apple.getWeight() / 1000); 
  7.         Thread.sleep(1000); 
  8.     } 
  9.     Date end = new Date(); 
  10.     log.info("苹果数量:{}个, 耗时:{}s", appleList.size(), (end.getTime() - begin.getTime()) /1000); 

输出的内容为耗时4s。

并行代码如下所示:

  1. List<Apple> appleList = initAppleList(); 
  2.  
  3. Date begin = new Date(); 
  4. appleList.parallelStream().forEach(apple -> 
  5.                                    { 
  6.                                        apple.setPrice(5.0 * apple.getWeight() / 1000); 
  7.                                        try { 
  8.                                            Thread.sleep(1000); 
  9.                                        } catch (InterruptedException e) { 
  10.                                            e.printStackTrace(); 
  11.                                        } 
  12.                                    } 
  13.                                   ); 
  14. Date end = new Date(); 
  15. log.info("苹果数量:{}个, 耗时:{}s", appleList.size(), (end.getTime() - begin.getTime()) /1000); 

输出结果为耗时1s。可以看到耗时大大提升了3s。

并行流拆分会影响流的速度

对于并行流来说需要注意以下几点:

对于 iterate 方法来处理的前 n 个数字来说,不管并行与否,它总是慢于循环的,

而对于 LongStream.rangeClosed() 方法来说,就不存在 iterate 的第两个痛点了。它生成的是基本类型的值,不用拆装箱操作,另外它可以直接将要生成的数字 1 - n 拆分成 1 - n/4, 1n/4 - 2n/4, ... 3n/4 - n 这样四部分。因此并行状态下的 rangeClosed() 是快于 for 循环外部迭代的

代码如下所示:

  1. package lambdasinaction.chap7; 
  2.  
  3. import java.util.stream.*; 
  4.  
  5. public class ParallelStreams { 
  6.  
  7.     public static long iterativeSum(long n) { 
  8.         long result = 0; 
  9.         for (long i = 0; i <= n; i++) { 
  10.             result += i; 
  11.         } 
  12.         return result; 
  13.     } 
  14.  
  15.     public static long sequentialSum(long n) { 
  16.         return Stream.iterate(1L, i -> i + 1).limit(n).reduce(Long::sum).get(); 
  17.     } 
  18.  
  19.     public static long parallelSum(long n) { 
  20.         return Stream.iterate(1L, i -> i + 1).limit(n).parallel().reduce(Long::sum).get(); 
  21.     } 
  22.  
  23.     public static long rangedSum(long n) { 
  24.         return LongStream.rangeClosed(1, n).reduce(Long::sum).getAsLong(); 
  25.     } 
  26.  
  27.     public static long parallelRangedSum(long n) { 
  28.         return LongStream.rangeClosed(1, n).parallel().reduce(Long::sum).getAsLong(); 
  29.     } 
  30.  
  31. package lambdasinaction.chap7; 
  32.  
  33. import java.util.concurrent.*; 
  34. import java.util.function.*; 
  35.  
  36. public class ParallelStreamsHarness { 
  37.  
  38.     public static final ForkJoinPool FORK_JOIN_POOL = new ForkJoinPool(); 
  39.  
  40.     public static void main(String[] args) { 
  41.         System.out.println("Iterative Sum done in: " + measurePerf(ParallelStreams::iterativeSum, 10_000_000L) + " msecs"); 
  42.         System.out.println("Sequential Sum done in: " + measurePerf(ParallelStreams::sequentialSum, 10_000_000L) + " msecs"); 
  43.         System.out.println("Parallel forkJoinSum done in: " + measurePerf(ParallelStreams::parallelSum, 10_000_000L) + " msecs" ); 
  44.         System.out.println("Range forkJoinSum done in: " + measurePerf(ParallelStreams::rangedSum, 10_000_000L) + " msecs"); 
  45.         System.out.println("Parallel range forkJoinSum done in: " + measurePerf(ParallelStreams::parallelRangedSum, 10_000_000L) + " msecs" ); 
  46.     } 
  47.  
  48.     public static <T, R> long measurePerf(Function<T, R> f, T input) { 
  49.         long fastest = Long.MAX_VALUE; 
  50.         for (int i = 0; i < 10; i++) { 
  51.             long start = System.nanoTime(); 
  52.             R result = f.apply(input); 
  53.             long duration = (System.nanoTime() - start) / 1_000_000; 
  54.             System.out.println("Result: " + result); 
  55.             if (duration < fastest) fastest = duration; 
  56.         } 
  57.         return fastest; 
  58.     } 

共享变量会造成数据出现问题

  1. public static long sideEffectSum(long n) { 
  2.     Accumulator accumulator = new Accumulator(); 
  3.     LongStream.rangeClosed(1, n).forEach(accumulator::add); 
  4.     return accumulator.total; 
  5.  
  6. public static long sideEffectParallelSum(long n) { 
  7.     Accumulator accumulator = new Accumulator(); 
  8.     LongStream.rangeClosed(1, n).parallel().forEach(accumulator::add); 
  9.     return accumulator.total; 
  10.  
  11. public static class Accumulator { 
  12.     private long total = 0; 
  13.  
  14.     public void add(long value) { 
  15.         total += value; 
  16.     } 

并行流的注意

  1. 尽量使用 LongStream / IntStream / DoubleStream 等原始数据流代替 Stream 来处理数字,以避免频繁拆装箱带来的额外开销
  2. 要考虑流的操作流水线的总计算成本,假设 N 是要操作的任务总数,Q 是每次操作的时间。N * Q 就是操作的总时间,Q 值越大就意味着使用并行流带来收益的可能性越大
  3. 对于较少的数据量,不建议使用并行流
  4. 容易拆分成块的流数据,建议使用并行流

 

责任编辑:武晓燕 来源: 小明菜市场
相关推荐

2022-02-23 10:54:37

电脑卡顿硬件

2009-06-18 12:21:07

javascriptdom

2011-01-12 11:22:24

微软认证

2021-11-03 06:28:21

Python运行速度开发

2018-08-02 16:17:34

Python 开发编程语言

2021-05-31 07:37:48

Windows10操作系统微软

2010-08-20 09:15:17

Ext JSJavaScript

2009-08-11 09:10:26

Windows 7系统提速

2012-10-24 09:40:46

网络优化系统优化

2020-05-21 16:19:43

iOS 13.5苹果运行速度

2023-10-26 08:33:16

Redis管道技术

2010-04-12 10:01:43

Windows 7运行速度

2010-01-20 13:19:01

VB.NET错误处理

2022-12-20 11:20:07

PeaZip 8开源压缩软件

2010-05-10 13:21:16

Windows 7系统日志

2018-06-27 09:00:00

Linux运行速度CPU信息

2016-02-22 09:27:18

2024-09-27 08:35:33

数组JavaScript性能

2009-09-12 09:43:29

Windows 7系统优化

2013-07-08 11:16:05

Windows 7
点赞
收藏

51CTO技术栈公众号