【Java并发编程 工作窃取】29.ForkJoin分支合并框架和Java8 Stream并行

简介

如果计算大单位的数值,可以使用ForkJoinPool来实现,它的原理是将一个大任务,通过递归拆分成很多小任务,每一个小任务就是一个线程来执行。


实现步骤:

  • 继承 RecursiveTask (有返回值)或者 RecursiveAction
  • 重写 compute 方法,并在里面写自己的 任务分支 任务合并逻辑
  • 调用方式:ForkJoinPool类中的invoke方法

ForkJoin的使用

通过ForkJoin计算0~1亿的总合:

public class ThreadForkJoinPool {
 
    public static void main(String[] args) {
        long end = 1 * 10000 * 10000;//计算一亿
 
        //ForkJoin框架计算
        Instant startTime = Instant.now();//java8新特性
        ForkJoinPool pool = new ForkJoinPool();
 
        ForkJoinTask<Long> task = new ForkJoinSumCalculate(0L,end);
 
        Long sum = pool.invoke(task);
        System.out.println("ForkJoin框架计算  得出结果:"+sum);
    }
}
class ForkJoinSumCalculate extends RecursiveTask<Long>{
 
    private long start;
 
    private long end;
 
    private static final long THURSHOLD = 10000L;//临界值,根据CPU设定
 
    public ForkJoinSumCalculate(long start, long end) {
        super();
        this.start = start;
        this.end = end;
    }
 
    @Override
    protected Long compute() {
        long length = end - start;
        if (length <= THURSHOLD) {//达到临界值运算结果
            long sum = 0L;
            for (long i = start;i <= end;i++) {
                sum += i;
            }
            return sum;
        } else {//未达到临界值 继续拆分
            long middle = (start + end)/2;
            ForkJoinSumCalculate left =new ForkJoinSumCalculate(start,middle);
            left.fork();//进行拆分,同时压入线程队列
            ForkJoinSumCalculate right =new ForkJoinSumCalculate(middle+1,end);
            right.fork();
            return left.join() + right.join();//对结果合并
        }
    }
 
}

打印结果:
ForkJoin框架计算 得出结果:5000000050000000

ForkJoin性能测试

ForkJoin 对比普通单线程 和java8 运算时间。

long end = 1 * 10000 * 10000;//计算一亿                                                                                   
 
//ForkJoin框架计算                                                                                                        
Instant startTime = Instant.now();//java8新特性                                                                          
ForkJoinPool pool = new ForkJoinPool();                                                                               
 
ForkJoinTask<Long> task = new ForkJoinSumCalculate(0L,end);                                                           
 
Long sum = pool.invoke(task);                                                                                         
 
Instant endTime = Instant.now();                                                                                      
System.out.println("ForkJoin框架计算  得出结果:"+sum+",计算时间:"+Duration.between(startTime, endTime).toMillis());//转毫秒          
 
//单线程for计算                                                                                                            
startTime = Instant.now();                                                                                            
long forSum = 0;                                                                                                      
for (long i = 0;i<= end;i++) {                                                                                        
    forSum += i;                                                                                                      
}                                                                                                                     
endTime = Instant.now();                                                                                              
System.out.println("单线程for计算  得出结果:"+forSum+",计算时间:"+Duration.between(startTime, endTime).toMillis());//转毫秒           
 
//java8 封装ForkJoin计算                                                                                                  
startTime = Instant.now();                                                                                            
long java8Sum = LongStream.rangeClosed(0L,end)                                                                        
.parallel()//并行流                                                                                                      
.reduce(0L,Long::sum);                                                                                                
endTime = Instant.now();                                                                                              
System.out.println("Java8 封装ForkJoin计算  得出结果:"+java8Sum+",计算时间:"+Duration.between(startTime, endTime).toMillis());//转毫秒

打印结果(计算结果不是绝对的,会随着机器不同而影响,java8性能最差?):

为了更细致的看结果:我们对这3种方式 通过如下比较:分别拆成3个方法,数据大小比较,cpu占用率比较:(这里不用JMX、Jstack工具了)
先看一下当前cpu占有率:
使用自己封装的forkJoin计算10亿:

long end = 1 * 10000 * 10000 * 10;//计算十亿
//ForkJoin框架计算
Instant startTime = Instant.now();//java8新特性
ForkJoinPool pool = new ForkJoinPool();
 
ForkJoinTask<Long> task = new ForkJoinSumCalculate(0L,end);
 
Long sum = pool.invoke(task);
 
Instant endTime = Instant.now();
System.out.println("ForkJoin框架计算  得出结果:"+sum+",计算时间:"+Duration.between(startTime, endTime).toMillis());//转毫秒

CPU占用率最高54%
打印输出:ForkJoin框架计算 得出结果:500000000500000000,计算时间:442


单线程性能测试

使用单线程的for循环计算10亿

long end = 1 * 10000 * 10000 * 10;//计算十亿
Instant startTime = Instant.now();
long forSum = 0;
for (long i = 0;i<= end;i++) {
    forSum += i;
}
Instant endTime = Instant.now();
System.out.println("单线程for计算  得出结果:"+forSum+",计算时间:"+Duration.between(startTime, endTime).toMillis());//转毫秒

CPU上升10个百分点。
打印输出:单线程for计算 得出结果:500000000500000000,计算时间:3099


java8性能测试

long end = 1 * 10000 * 10000 * 10;//计算十亿                                                                              
Instant startTime = Instant.now();                                                                                    
long java8Sum = LongStream.rangeClosed(0L,end)                                                                        
.parallel()//并行流                                                                                                      
.reduce(0L,Long::sum);                                                                                                
Instant endTime = Instant.now();                                                                                      
System.out.println("Java8 封装ForkJoin计算  得出结果:"+java8Sum+",计算时间:"+Duration.between(startTime, endTime).toMillis());//转毫秒

cpu占用率:91%
打印输出:Java8 封装ForkJoin计算 得出结果:500000000500000000,计算时间:2266


计算0~100万总和:
ForkJoin框架计算 得出结果:500000500000,计算时间:7
单线程for计算 得出结果:500000500000,计算时间:5
Java8 封装ForkJoin计算 得出结果:500000500000,计算时间:69

数据量越少单线程的优势就体现出来了

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

terrybg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值