ForkJoin的使用及for循环stream并行流三种方式的时间比较

Posted 若曦`

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ForkJoin的使用及for循环stream并行流三种方式的时间比较相关的知识,希望对你有一定的参考价值。

1. ForkJoin简介

ForkJoin的作用是将一个任务划分成多个小任务,让多个线程去并行执行一个任务,在一些高并发的场景可能会用到

ForkJoin框架提供了一个ForkJoinPool (ForkJoin线程池),当任务没有指定线程的时候,就会使用默认的ForkJoin线程池

ForkJoin使用了递归算法和分治思想,Forkjoin需要在多核cpu下才会奏效,因为使用的是并行处理的方式,核心思想是工作窃取

工作窃取

当一个线程执行完任务后,去窃取其他线程的任务做
在这里插入图片描述

2. ForkJoin的使用

使用步骤

(1) 创建一个任务类继承 RecursiveTask抽象类,并重写里面的compute() 方法,在compute()中使用递归算法、分治思想

(2) 在主方法中的操作

① 创建ForkJoin线程池(new ForkJoinPool())

② 将任务提交给线程池 ForkJoinTask sumbit = forkJoinPool.summit();

③ 获取结果 (submit.get())

代码示例

(求1~10亿总和的值)

//(1) 继承RecursiveTask抽象类,重写compute()方法(使用递归算法)
public class MyForkJoinTask extends RecursiveTask<Long>{

        //记录线程开始的值
        private Long startValue;
        //记录结束的值
        private Long endValue;
        //最小拆分的值
        private Long MAX = 100_0000L;

        MyForkJoinTask(Long startValue,Long endValue){
            this.startValue = startValue;
            this.endValue = endValue;
        }

        @Override
        protected Long compute() {
            Long res=0L;
            //如果拆分的值小于max了,则开始任务
            if(endValue-startValue<MAX){
//                System.out.println("任务开始的值:"+startValue+"====任务结束的值:"+endValue);
                res = 0L;
                for (long i = startValue; i < endValue; i++) {
                    res+=i;
                }
            }
            //如果拆分的值比max大,则继续拆分
            else {
                //将前一半的任务给joinTask1
                MyForkJoinTask joinTask1 = new MyForkJoinTask(startValue,(startValue+endValue)/2);
                //将后一半任务给joinTask2
                MyForkJoinTask joinTask2 = new MyForkJoinTask((startValue+endValue)/2,endValue);
                //同时开启任务线程1和线程2
                invokeAll(joinTask1,joinTask2);
                return joinTask1.join()+joinTask2.join();
            }
            return res;
        }
        
        //2. 主方法中使用ForkJoin
    	public static void main(){
        //获取开始时间
        long start = System.currentTimeMillis();
        Long res=0L;
        //(1) 创建ForkJoin的线程池
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        //(2) 开启任务
        ForkJoinTask<Long> submit = forkJoinPool.submit(new MyForkJoinTask(1L, 10_0000_0000L));
        try {
            //(3) 获取结果
            res  = submit.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        //结束时间
        long end = System.currentTimeMillis();
        System.out.println("ForkJoin方式的结果是:"+res);
        System.out.println("花费的时间是:"+(end-start));
    }
}

3. 三种方式的效率比较

比较求1~10亿总和的运行时间

(1) 普通方式(For循环)

 public static void normal(){
        //获取开始时间
        long start = System.currentTimeMillis();
        long res=0;
        for (int i = 0; i < 10_0000_0000; i++) {
            res+=i;
        }
        //结束时间
        long end = System.currentTimeMillis();
        System.out.println("普通方式的结果是:"+res);
        System.out.println("花费的时间是:"+(end-start));
        System.out.println("==============");
    }

(2) ForkJoin

public static void forkJoin(){
        //获取开始时间
        long start = System.currentTimeMillis();
        Long res=0L;
        //1. 创建ForkJoin的线程池
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        //2. 开启任务
        ForkJoinTask<Long> submit = forkJoinPool.submit(new MyForkJoinTask(1L, 10_0000_0000L));
        try {
            //3.获取结果
            res  = submit.get();
//            System.out.println("结果是:"+res);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        //结束时间
        long end = System.currentTimeMillis();
        System.out.println("ForkJoin方式的结果是:"+res);
        System.out.println("花费的时间是:"+(end-start));
        System.out.println("========================");
    }

(3) Stream并行流

 public static void testStream(){
        Long start = System.currentTimeMillis();
        //parallel表示并行运算 Long::sum调用Long下的求和方法
        Long res = LongStream.range(0,10_0000_0000L).parallel().reduce(0,Long::sum);
        Long end= System.currentTimeMillis();
        System.out.println("stram流方式的结果是:"+res);
        System.out.println("花费的时间是:"+(end-start));
    }

在这里插入图片描述
运行效率 Stream流>for循环>ForkJoin

可能是算法不好导致ForkJoin比for循环慢,不大理解这一点

以上是关于ForkJoin的使用及for循环stream并行流三种方式的时间比较的主要内容,如果未能解决你的问题,请参考以下文章

JUC并发编程--- ForkJoin与Stream并行流

并发与并行同步与异步,线程安全的实现

并发与并行同步与异步,线程安全的实现

并发与并行同步与异步,线程安全的实现

并发与并行同步与异步,线程安全的实现

java8 stream 比for循环快吗