转:http://blog.csdn.net/sunjin9418/article/details/53143588

将一个顺序执行的流转变成一个并发的流只要调用 parallel()方法
public static long parallelSum(long n){
    return Stream.iterate(1L, i -> i +1).limit(n).parallel().reduce(0L,Long::sum);
}
并行流就是一个把内容分成多个数据块,并用不不同的线程分别处理每个数据块的流。最后合并每个数据块的计算结果。
将一个并发流转成顺序的流只要调用sequential()方法
stream.parallel() .filter(...) .sequential() .map(...) .parallel() .reduce();
 
这两个方法可以多次调用, 只有最后一个调用决定这个流是顺序的还是并发的。
 
并发流使用的默认线程数等于你机器的处理器核心数。
 
通过这个方法可以修改这个值,这是全局属性。
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "12");
 
并非使用多线程并行流处理数据的性能一定高于单线程顺序流的性能,因为性能受到多种因素的影响。
如何高效使用并发流的一些建议:
1. 如果不确定, 就自己测试。
2. 尽量使用基本类型的流  IntStream, LongStream, and DoubleStream
3. 有些操作使用并发流的性能会比顺序流的性能更差,比如limit,findFirst , 依赖元素顺序的操作在并发流中是极其消耗性能的 。findAny的性能就会好很多,应为不依赖顺序。
4. 考虑流中计算的性能(Q)和操作的性能(N)的对比, Q表示单个处理所需的时间, N表示需要处理的数量,如果Q的值越大, 使用并发流的性能就会越高。
5. 数据量不大时使用并发流,性能得不到提升。
6.考虑数据结构:并发流需要对数据进行分解,不同的数据结构被分解的性能时不一样的。
 
流的数据源和可分解性
可分解性
ArrayList 非常好
LinkedList
IntStream.range 非常好
Stream.iterate
HashSet
TreeSet
 
 
7. 流的特性以及中间操作对流的修改都会对数据对分解性能造成影响。 比如固定大小的流在任务分解的时候就可以平均分配,但是如果有filter操作,那么流就不能预先知道在这个操作后还会剩余多少元素。
 
8. 考虑最终操作的性能:如果最终操作在合并并发流的计算结果时的性能消耗太大,那么使用并发流提升的性能就会得不偿失。
 
9.需要理解并发流实现机制:
 
fork/join 框架
 
fork/join框架是jdk1.7引入的,java8的stream多线程并非流的正是以这个框架为基础的,所以想要深入理解并发流就要学习fork/join框架。
fork/join框架的目的是以递归方式将可以并行的任务拆分成更小的任务,然后将每个子任务的结果合并起来生成整体结果。它是ExecutorService接口的一个实现,它把子任务分配线程池(ForkJoinPool)中的工作线程。要把任务提交到这个线程池,必须创建RecursiveTask<R>的一个子类,如果任务不返回结果则是RecursiveAction的子类。
 
fork/join框架流程示意图:
 
 
废话不多说,上代码:
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.stream.LongStream;

/**
 * Created by sunjin on 2016/7/5.
 * 继承RecursiveTask来创建可以用于分支/合并的框架任务
 */
public class ForkJoinSumCalculator extends RecursiveTask<Long> {
    //要求和的数组
    private final long[] numbers;
    //子任务处理的数组开始和终止的位置
    private final int start;
    private final int end;
    //不在将任务分解成子任务的阀值大小
    public static final int THRESHOLD = 10000;

//用于创建组任务的构造函数
    public ForkJoinSumCalculator(long[] numbers){
        this(numbers, 0, numbers.length);
    }

//用于递归创建子任务的构造函数
    public ForkJoinSumCalculator(long[] numbers,int start,int end){
        this.numbers = numbers;
        this.start = start;
        this.end = end;
    }

//重写接口的方法
    @Override
    protected Long compute() {
        //当前任务负责求和的部分的大小
        int length = end start;
        //如果小于等于阀值就顺序执行计算结果
        if(length <= THRESHOLD){
            return computeSequentially();
        }
        //创建子任务来为数组的前一半求和
        ForkJoinSumCalculator leftTask = new ForkJoinSumCalculator(numbersstartstart + length/2);
        //将子任务拆分出去,丢到ForkJoinPool线程池异步执行。
        leftTask.fork();
        //创建子任务来为数组的后一半求和
        ForkJoinSumCalculator rightTask = new ForkJoinSumCalculator(numbersstart + length/2, end);
        //第二个任务直接使用当前线程计算而不再开启新的线程。
        long rightResult = rightTask.compute();
        //读取第一个子任务的结果,如果没有完成则等待。
        long leftResult = leftTask.join();
        //合并两个子任务的计算结果
        return rightResult + leftResult;
    }

//顺序执行计算的简单算法
    private long computeSequentially(){
        long sum = 0;
        for(int i =start; i< end; i++){
            sum += numbers[i];
        }
        return sum;
    }
    //提供给外部使用的入口方法
    public static long forkJoinSum(long n) {
        long[] numbers = LongStream.rangeClosed(1, n).toArray();
        ForkJoinTask<Long> task = new ForkJoinSumCalculator(numbers);
        return new ForkJoinPool().invoke(task);
    }
}

注意事项:
1. 调用join 方法要等到调用这个方法的线程的自己的任务完成之后。
2. 不要直接去调用ForkJoinPool的invoke方法 ,只需要调用RecursiveTask的fork或者compute。
3. 拆解任务时只需要调用一次fork执行其中一个子任务, 另一个子任务直接利用当前线程计算。应为fork方法只是在ForkJoinPool中计划一个任务。
4.任务拆分的粒度不宜太细,不否得不偿失。
 
 
工作盗取
由于各种因素,即便任务拆分是平均的,也不能保证所有子任务能同时执行结束, 大部分情况是某些子任务已经结束, 其他子任务还有很多, 在这个时候就会有很多资源空闲, 所以fork/join框架通过工作盗取机制来保证资源利用最大化, 让空闲的线程去偷取正在忙碌的线程的任务。
在没有任务线程中的任务存在一个队列当中, 线程每次会从头部获取一个任务执行,执行完了再从queue的头部获取一个任务,直到队列中的所有任务执行完,这个线程偷取别的线程队列中的任务时会从队列到尾部获取任务,并且执行,直到所有任务执行结束。
从这个角度分析,任务的粒度越小, 资源利用越充分。
 
 
工作盗取示意图
 
 
可拆分迭代器Spliterator
 
它和Iterator一样也是用于遍历数据源中的元素,但是他是为并行执行而设计的。 java8 所有数据结构都实现了 这个接口, 一般情况不需要自己写实现代码。但是了解它的实现方式会让你对并行流的工作原理有更深的了解。(未完待续)

java8新特性(六):Stream多线程并行数据处理的更多相关文章

  1. 乐字节-Java8新特性之Stream流(上)

    上一篇文章,小乐给大家介绍了<Java8新特性之方法引用>,下面接下来小乐将会给大家介绍Java8新特性之Stream,称之为流,本篇文章为上半部分. 1.什么是流? Java Se中对于 ...

  2. Java8 新特性之Stream API

    1. Stream 概述 Stream 是Java8中处理集合的关键抽象概念,可以对集合执行非常复杂的查找,过滤和映射数据等操作; 使用 Stream API 对集合数据进行操作,就类似于使用 SQL ...

  3. 【Java8新特性】- Stream流

    Java8新特性 - Stream流的应用 生命不息,写作不止 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! ...

  4. 【Java8新特性】Stream API有哪些中间操作?看完你也可以吊打面试官!!

    写在前面 在上一篇<[Java8新特性]面试官问我:Java8中创建Stream流有哪几种方式?>中,一名读者去面试被面试官暴虐!归根结底,那哥儿们还是对Java8的新特性不是很了解呀!那 ...

  5. Java8 新特性 Lambda & Stream API

    目录 Lambda & Stream API 1 Lambda表达式 1.1 为什么要使用lambda表达式 1.2 Lambda表达式语法 1.3 函数式接口 1.3.1 什么是函数式接口? ...

  6. Java8新特性之Stream

    原文链接:http://ifeve.com/stream/ Java8初体验(二)Stream语法详解 感谢同事[天锦]的投稿.投稿请联系 tengfei@ifeve.com上篇文章Java8初体验( ...

  7. Java8新特性--流(Stream)

    1.简介      Java 8是Java自Java 5(发布于2004年)之后的最重要的版本.这个版本包含语言.编译器.库.工具和JVM等方面的十多个新特性.在本文中我们一起来学习引入的一个新特性- ...

  8. java8新特性五-Stream

    继上次学习过Java8中的非常重要的Lambda表达式之后,接下来就要学习另一个也比较重要的知识啦,也就如标题所示:Stream,而它的学习是完全依赖于之前学习的Lambda表达式. Java 8 A ...

  9. java8新特性六-Optional 类

    Optional 类是一个可以为null的容器对象.如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象. Optional 是个容器:它可以保存类型T的值,或者仅仅保 ...

随机推荐

  1. mysql8.0 Authentication plugin 'caching_sha2_password' cannot be loaded

    安装mysql8.0后使用navicat创建连接, 然后报如题所示警告.可参考如下解决方案: https://stackoverflow.com/questions/49194719/authenti ...

  2. 【脚下生根】之深度探索安卓OpenGL投影矩阵

    世界变化真快,前段时间windows开发技术热还在如火如荼,web技术就开始来势汹汹,正当web呈现欣欣向荣之际,安卓小机器人,咬过一口的苹果,winPhone开发平台又如闪电般划破了混沌的web世界 ...

  3. JAXBContext处理CDATA

    今天做Lucene数据源接口时,遇到一个问题,就是输出xml时将某些数据放在CDATA区输出: 1.依赖的jar包,用maven管理项目的话, <dependency> <group ...

  4. Intellij使用-- 导入Eclipse的代码格式化文件

    目录[-] 方法 安装插件: 配置插件: 使用插件 测试 对于一个团队来说,使用统一的代码格式是非常重要的,否则在使用版本控制工具时,会出现大量的冲突.在Eclipse里,我们可以通过一些xml来进行 ...

  5. Docker K8s基本概念入门

    原文地址:https://blog.csdn.net/TM6zNf87MDG7Bo/article/details/79621510 k8s是一个编排容器的工具,其实也是管理应用的全生命周期的一个工具 ...

  6. Runway for Mac(UML 流程图绘图工具)破解版安装

    1.软件简介    Runway 是 macOS 系统上一款强大实用的软件开发工具,Runway for Mac 是一个界面简单功能强大的UML设计师.此外,Runway for Mac 带给你所有你 ...

  7. bootstrap-table 的 toolbar 能去掉显示吗?

    我想禁用所有的toolbar,因为我显示的要求很简单,所以不想要所有的toolbar,这样可以省掉一行,但找不到方法.谢谢! data-show-columns="false"就行 ...

  8. ListView点击Item展开隐藏项(单项展开、多项展开、复杂布局时的展开处理)

    手机屏幕毕竟有限,当我们要显示较多数据时便不得不舍去一些次要信息.将主要信息优先显示,也使显示效果更加简洁美观.遇到类似的需求,我们使用最多的就是 ListView ,而假设每次点击一个 Item 都 ...

  9. CListCtrl选中某行

    原文链接: http://blog.csdn.net/wxq1987525/article/details/7461461 1.设置CListCtrl选中行 m_list.SetItemState( ...

  10. 测试rp文件

    正文: 测试我的博客,我的资源在github上! 地址:https://github.com/gmqllf/tx-lcn