zoukankan      html  css  js  c++  java
  • JAVA8的parallelStream

    JAVA8中引入了lamda表达式和Stream接口。
    用法:
    public static void main(String[] args) {
       List list = new ArrayList();
       for(int i = 0; i < 100; i++){
          list.add(i);
       }
    
       list.parallelStream().forEach(x -> {
          System.out.println(x);
       });
    }
    

     输出结果是无序的。

    代码上 stream 和 parallelStream 语法差异较小,从执行结果来看,stream顺序输出,而parallelStream 无序输出;parallelStream 执行耗时是 stream 的五分之一。
    可以看到在当前测试场景下,parallelStream 获得的相对较好的执行性能,那parallelStream背后到底是什么呢?
    要深入了解parallelStream,首先要弄明白ForkJoin框架和ForkJoinPool。ForkJoin框架是java7中提供的并行执行框架,他的策略是分而治之。说白了,就是把一个大的任务切分成很多小的子任务,子任务执行完毕后,再把结果合并起来。


    顺便说下ForkJoin框架和ThreadPoolExecutor的区别,ForkJoin框架可以使用数量有限的线程数,执行大量任务,并且这些任务之间是有父子依赖的,必须是子任务执行完成后,父任务才能执行。ThreadPoolExecutor 显然是无法支持这种场景的。而ForkJoin框架,可以让其中的线程创建新的任务,并挂起当前的任务,任务以及子任务会保留在一个内部队列中,此时线程就能够从队列中选择任务顺序执行。

    Java 8为ForkJoinPool添加了一个通用线程池,这个线程池用来处理那些没有被显式提交到任何线程池的任务。它是ForkJoinPool类型上的一个静态元素,它拥有的默认线程数量等于运行计算机上的处理器数量。当调用Arrays类上添加的新方法时,自动并行化就会发生。比如用来排序一个数组的并行快速排序,用来对一个数组中的元素进行并行遍历。自动并行化也被运用在Java 8新添加的Stream API中。

    上面的代码中,forEach方法会为每个元素的操作创建一个任务,该任务会被前文中提到的ForkJoinPool中的通用线程池处理。以上的并行计算逻辑当然也可以使用ThreadPoolExecutor完成,但是就代码的可读性和代码量而言,使用ForkJoinPool明显更胜一筹。

    默认线程池的数量就是处理器的数量,特殊场景下可以使用系统属性:-Djava.util.concurrent.ForkJoinPool.common.parallelism={N} 调整。

    并不是并行执行就是性能最好的,要根据具体的应用场景测试分析。如果每个子任务执行时间较短,线程切换就会消耗了大量时间,很明显不适合用这个方法。


    说到了并发,不得不提线程安全。先看一个例子:

    public void doThreadUnSafe() {
            List<Integer> listFor = new ArrayList<>();
            List<Integer> listParallel = new ArrayList<>();
    
            IntStream.range(0, 1000).forEach(listFor::add);
            IntStream.range(0, 1000).parallel().forEach(listParallel::add);
    
            System.out.println("listFor size :" + listFor.size());
            System.out.println("listParallel size :" + listParallel.size());
        }


    输出结果:

    listFor size :1000
    listParallel size :949

    显而易见,stream.parallel.forEach()中执行的操作并非线程安全。如果需要线程安全,可以把集合转换为同步集合,即:Collections.synchronizedList(new ArrayList<>())。

    总结下来如下:

      1. 使用parallelStream可以简洁高效的写出并发代码。
      2. parallelStream并行执行是无序的。
      3. parallelStream提供了更简单的并发执行的实现,但并不意味着更高的性能,它是使用要根据具体的应用场景。如果cpu资源紧张parallelStream不会带来性能提升;如果存在频繁的线程切换反而会降低性能。
      4. 任务之间最好是状态无关的,因为parallelStream默认是非线程安全的,可能带来结果的不确定性。

    参考:https://zhuanlan.zhihu.com/p/43039062

  • 相关阅读:
    echo "http://172.17.26.115:8380/?key=%c8%fd%d0%c7%ca%d6%bb%fa%b1%f9%cf%e4" | mail -s "noresult_monitr error" maolingzhi@jd.com
    python实现的文本编辑器
    PyQt写的浏览单web页面的browser
    中非发展基金
    团队介绍
    微众—国内最大的微信公众服务平台
    微软创投加速器简介
    知方可补不足~SQL为大数据引入分区表
    实时监控Cat之旅~介绍与自定义类型在哪里
    EF架构~Cannot attach the file as database
  • 原文地址:https://www.cnblogs.com/liuyuan1227/p/14286244.html
Copyright © 2011-2022 走看看