转: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. Debug 路漫漫-05

    Debug 路漫漫-05: 1.使用这种方式计算 AUC 指标,结果出来居然是 NAN, —— 分母为(M*N),M或者N必有一个为0 了.(nan出现的情况绝大部分是分母出现0了)   若分子为0的 ...

  2. [转]解决Eclipse中编辑xml文件的智能提示问题

    转自:http://hi.baidu.com/cghroom/item/48fd2d0dc1fc23c675cd3c3e 摘要:  Eclipse for Android xml 文件代码自动提示功能 ...

  3. 谈UIView Animation编程艺术

    一.大小动画(改变frame) -(void)changeFrame{ CGRect originalRect = self.anView.frame; CGRect rect = CGRectMak ...

  4. 安装apr报错rm: cannot remove `libtoolT': No such file or directory

    直接打开/usr/local/src/apr-1.4.6/configure  把 $RM“$cfgfile” 那行删除掉 $RM“$cfgfile” 大约在 42302行 然后再重新运行  ./co ...

  5. XP、Windows7下自动关机vbs脚本,使用windows计划任务+vbs脚本在XP、Windows7下实现定时自动关机

    VBScript(Microsoft Visual Basic Script Edition).,微软公司可视化BASIC脚本版). 正如其字面所透露的信息, VBS(VBScript的进一步简写)是 ...

  6. Linux安装ElasticSearch-2.2.0-分词器插件(Mmseg)

    1.在gitpub上搜索elasticsearch-analysis,能够看到所有elasticsearch的分词器: 2.安装Mmseg分词器:https://github.com/medcl/el ...

  7. 你真的了解字典(Dictionary)吗? C# Memory Cache 踩坑记录 .net 泛型 结构化CSS设计思维 WinForm POST上传与后台接收 高效实用的.NET开源项目 .net 笔试面试总结(3) .net 笔试面试总结(2) 依赖注入 C# RSA 加密 C#与Java AES 加密解密

    你真的了解字典(Dictionary)吗?   从一道亲身经历的面试题说起 半年前,我参加我现在所在公司的面试,面试官给了一道题,说有一个Y形的链表,知道起始节点,找出交叉节点.为了便于描述,我把上面 ...

  8. mysql - tmp_table_size & max_heap_table_size

    Command-Line Format --tmp_table_size=# System Variable Name tmp_table_size Variable Scope Global, Se ...

  9. ADF_Starting系列3_使用ADF开发富Web应用程序之开发User Interface

    内容中包含 base64string 图片造成字符过多,拒绝显示

  10. SharePoint 2013 Step by Step——How to Create a Lookup Column to Another Site(Cross Site)

    OverView In this post,I want to show u how to add a look up column in my list or library that looks ...