Java--8--新特性--串并行流与ForkJoin框架

并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。穿行流则相反,并行流的底层其实就是ForkJoin框架的一个实现。

那么先了解一下ForkJoin框架吧。

Fork/Join框架:在必要的情况下,将一个大任务,进行拆分(fork) 成若干个子任务(拆到不能再拆,这里就是指我们制定的拆分的临界值),再将一个个小任务的结果进行join汇总。

Fork/Join与传统线程池的区别!

Fork/Join采用“工作窃取模式”,当执行新的任务时他可以将其拆分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随即线程中偷一个并把它加入自己的队列中。

就比如两个CPU上有不同的任务,这时候A已经执行完,B还有任务等待执行,这时候A就会将B队尾的任务偷过来,加入自己的队列中,对于传统的线程,ForkJoin更有效的利用的CPU资源!

我们来看一下ForkJoin的实现:实现这个框架需要继承RecursiveTask 或者 RecursiveAction ,RecursiveTask是有返回值的,相反Action则没有

package ForkJionP;


import java.util.concurrent.RecursiveTask;

public class ForkJoinWork extends RecursiveTask<Long> {

    private Long start;//起始值
    private Long end;//结束值
    public static final  Long critical = 100000L;//临界值

    public ForkJoinWork(Long start, Long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        //判断是否是拆分完毕
        Long lenth = end - start;
        if(lenth<=critical){
            //如果拆分完毕就相加
            Long sum = 0L;
            for (Long i = start;i<=end;i++){
                sum += i;
            }
            return sum;
        }else {
            //没有拆分完毕就开始拆分
            Long middle = (end + start)/2;//计算的两个值的中间值
            ForkJoinWork right = new ForkJoinWork(start,middle);
            right.fork();//拆分,并压入线程队列
            ForkJoinWork left = new ForkJoinWork(middle+1,end);
            left.fork();//拆分,并压入线程队列

            //合并
            return right.join() + left.join();
        }
    }
}

 

测试:

package ForkJionP;

import org.junit.Test;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;

public class ForkJoinWorkDemo {

    public  void test() {
    //ForkJoin实现
long l = System.currentTimeMillis(); ForkJoinPool forkJoinPool = new ForkJoinPool();//实现ForkJoin 就必须有ForkJoinPool的支持 ForkJoinTask<Long> task = new ForkJoinWork(0L,10000000000L);//参数为起始值与结束值 Long invoke = forkJoinPool.invoke(task); long l1 = System.currentTimeMillis(); System.out.println("invoke = " + invoke+" time: " + (l1-l)); //invoke = -5340232216128654848 time: 76474 } public void test2(){
  //普通线程实现 Long x
= 0L; Long y = 10000000000L; long l = System.currentTimeMillis(); for (Long i = 0L; i <= y; i++) { x+=i; } long l1 = System.currentTimeMillis(); System.out.println("invoke = " + x+" time: " + (l1-l)); //invoke = -5340232216128654848 time: 160939 } @Test public void test3(){
   //Java 8 并行流的实现
long l = System.currentTimeMillis(); long reduce = LongStream.rangeClosed(0, 10000000000L).parallel().reduce(0, Long::sum); long l1 = System.currentTimeMillis(); System.out.println("invoke = " + reduce+" time: " + (l1-l)); //invoke = -5340232216128654848 time: 15531 } }

 我们观察上面可以看出来执行10000000000L的相加操作各自执行完毕的时间不同。观察到当数据很大的时候ForkJoin比普通线程实现有效的多,但是相比之下ForkJoin的实现实在是有点麻烦,这时候Java 8 就为我们提供了一个并行流来实现ForkJoin实现的功能。可以看到并行流比自己实现ForkJoin还要快

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

 

posted @ 2017-11-29 16:07  Bap  阅读(20620)  评论(0编辑  收藏  举报