Java8新特性之并行流(三)

来源:互联网 发布:php微商城源码 编辑:程序博客网 时间:2024/05/22 07:59

转载:http://blog.csdn.net/liudongdong0909/article/details/77435079

一、什么是并行流


1.1串行,并行,并发的区别

串行,即按序执行每个线程的任务会使程序效率很低下,与之相对的是并行与并发。并行是指多(核)cpu同时处理多个线程,每个cpu单独负责一个线程,有多少个cpu,就可以并行的执行多少线程。而并发是指多个线程在宏观(相对于较长的时间区间而言)上表现为同时执行,非并行的并发由一个cpu通过轮流执行每个线程的一部分来实现。下图是我


1

我们之前很多地方用到了流操作来对集合进行遍历操作。默认情况下流是串行的,效率较低。为了提高流的效率,可以将串行流变成并行流,只需要调用一下parallel方法即可


1.2 并行流 : 就是把一个内容分成多个数据块,并用不同的线程分 别处理每个数据块的流。

Java 8 中将并行进行了优化,我们可以很容易的对数据进行并 行操作。Stream API 可以声明性地通过 parallel() sequential() 在并行流与顺序流之间进行切换。

二、了解 Fork/Join框架

Fork/Join 框架 : 就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个 小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总.
这里写图片描述

三、Fork/Join 框架与传统线程池的区别

采用 “工作窃取”模式(work-stealing): 当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线 程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。

相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的 处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些原因 无法继续运行,那么该线程会处于等待状态。而在fork/join框架实现中,如果 某个子问题由于等待另外一个子问题的完成而无法继续运行.那么处理该子 问题的线程会主动寻找其他尚未运行的子问题来执行。这种方式减少了线程 的等待时间, 高了性能。

四、 案例

4.1 java8之前 Fork/Join的计算

创建一个ForkJoinCalculate计算类:

public class ForkJoinCalculate extends RecursiveTask<Long> {   private long start;   private long end;   private static final long THRESHOLD = 1000000;   public ForkJoinCalculate(long start, long end) {       this.start = start;       this.end = end;   }   @Override   protected Long compute() {       long length = end - start;       if (length <= THRESHOLD) {           long sum = 0;           for (long i = start; i <= end; i++) {               sum += i;           }           return sum;       }else {           long middle = (start + end) / 2;           ForkJoinCalculate left = new ForkJoinCalculate(start, middle);           left.fork();           ForkJoinCalculate right = new ForkJoinCalculate(middle + 1, end);           right.fork();           return left.join() + right.join();       }   }}

测试方法:

private static final long END_VALUE = 10000000000L;// fork join@Testpublic void test1(){   Instant start = Instant.now();   ForkJoinPool pool = new ForkJoinPool();   ForkJoinTask<Long> task = new ForkJoinCalculate(0, END_VALUE);   Long sum = pool.invoke(task);   System.out.println(sum);   Instant end = Instant.now();   System.out.println("耗时:" + Duration.between(start, end).toMillis());}

执行结果:

-5340232216128654848耗时:2325
  • 1
  • 2

4.2 使用普通for 循环:

@Testpublic void test2(){    Instant start = Instant.now();    long sum = 0L;    for (long i = 0; i <= END_VALUE; i ++){        sum += i;    }    System.out.println(sum);    Instant end = Instant.now();    System.out.println("耗时:" + Duration.between(start, end).toMillis());}

执行结果:

-5340232216128654848耗时:3571

4.3 java8中 Fork/Join计算

//java8 的并行流测试@Testpublic  void test3(){    Instant start = Instant.now();    LongStream.rangeClosed(0, END_VALUE)            .parallel()            .reduce(0, Long::sum);    Instant end = Instant.now();    System.out.println("耗时为:" + Duration.between(start, end).toMillis());}

执行结果:

耗时为:1690

检查本机的可用处理器数:

 // 可用处理器 @Test public  void test4(){     int num = Runtime.getRuntime().availableProcessors();     System.out.println(num); }

执行结果:

8

最终结果可能有一定的误差,这里只是为了测试 java8的Fork/Join, 时间上来说也是比较明显的。不具有肯定的说明型。

本机配置: 4核8线程。固态硬盘。 请注意调整 END_VALUE 值得大小。。。。。。。。

使用并行时,在多个内核直接移动数据的代价也比较大,因此,要保证在内核中并行执行工作的时间比在内核之间传输数据的时间要长才比较划算。

现在我们来总结一下如何高效使用并行流:

  • 如果用循环还是顺序流或者是并行流,像我们上面那样测试一下;
  • 注意装箱,尽量使用`IntStream`, `LongStream`,和`DoubleStream`来避免装箱拆箱;
  • 有些操作在并行流上性能很差,比如`limit`,`findFirst`等依赖顺序的操作。`unordered`方法可以把有序流转为无序流,使用`findAny`等好很多,在无序流上用`limit`也好很多;
  • 计算流水线操作总成本,处理单个元素用时越多,并行就越划算;
  • 对于较小的数据量,用并行不一定是好事儿;
  • 数据结果是否易于分解,比如`ArrayList`比`LinkedList`易于分解,`range`创建的原始流也易于分解;
  • 终端操作中的合并大家是否很大,大了也不划算。



阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 调兵山吧 河南南阳淅川 淅川买房 淅川公司 淅川在线 淅川论坛 淅川是哪里 淅川帖吧 淅川老城吧 淅川说吧 淅川九重吧 淅川第一生活网 淅川求职招聘网 鹤壁市淇县宾馆 淇滨区 鹤壁市淇滨区邮编 鹤壁淇滨区二手房出售 鹤壁市淇滨区 鹤壁淇滨区 淇澳岛 淇澳岛旅游 珠海淇澳岛 淇澳岛一日游攻略 淇澳岛游玩攻略 珠海淇澳岛一日游 淇澳岛旅游攻略 淇澳岛红树林 淇澳岛沙滩 珠海淇澳岛旅游攻略 淇澳岛有什么好玩的 珠海荷包岛 珠海荷包岛旅游 荷包岛图片 珠海荷包岛旅游攻略 一生挚爱 淇老游 简童沈修瑾 淇老游 爱恨两痛 淇老游 蚀骨危情 淇老游 爱恨两痛简童 淇老游 淋巴堵 淋巴堵的危害