JUC_ForkJoin

概述

Fork/Join框架:Java1.7中提供的并行执行任务框架,具体模式为:将主任务逐级拆分为多级子任务并行执行,最终通过结果合并得到主任务的执行结果

图示

ForkJoinTask

ForkJoinTask:基本任务类,创建任务类必须继承基本任务类的子类,重写compute方法;提供fork方法,join方法与get方法

主要子类:

RecursiveTask: 用于有返回结果的任务
RecursiveAction: 用于无返回结果的任务
CountedCompleter: 用于无返回结果的任务 特殊之处在于任务执行完compute方法后就结束执行 并不会等待子任务执行完成 同级子任务均执行完成后可触发结果合并 并将合并结果提交给上级任务

fork方法:

将子任务放入当前工作线程的双端队列 volatile WorkQueue[] workQueues;

join方法:

阻塞等待至成功获取任务的执行结果 通常用于向上级任务提交任务的执行结果(若任务有子任务 则阻塞等待至所有子任务都执行完成 此时获取的执行结果为所有子任务执行结果的合并结果)

get方法:

与join方法大同小异 区别在于get方法可以抛出ExecutionException与InterruptedException异常 通常用于获取最终的执行结果(主任务的执行结果 task.get() task为任务类实例或submit方法的返回值)

ForkJoinPool

ForkJoinPool :ExecutorService 的实现类,用于执行 ForkJoinTask(亦可以接收Runnable/Callable 任务,只是在运行时均会被包装为 ForkJoinTask )

线程池中的一个工作线程(ForkJoinWorkerThread)不能同时执行多个任务

线程池不会为每个子任务创建单独的工作线程,Fork/Join框架将根据当前正在并发执行的工作线程的状态决定:将子任务放入当前线程的双端队列,或创建一个新的工作线程执行子任务,或唤醒正在等待任务的工作线程执行子任务

Fork/Join框架使用工作窃取机制( Work-Stealing )平衡线程的工作负载

工作窃取( Work-Stealing )

工作窃取:闲置的线程尝试从忙碌的线程的双端队列中窃取工作

通常情况下,工作线程从自身的双端队列中获取任务,但当自身的双端队列为空时,就会尝试从其他忙碌的线程的双端队列尾部或全局入口处获取任务,以此提高整体的执行效率

执行方法

方法特点
invoke()阻塞至任务及其所有子任务执行完成才返回,即同步执行,返回值为task.join()
submit()立刻返回,即异步执行,返回值为task
execute()立刻返回,即异步执行,无返回值

注:task为主任务

示例:以 RecursiveTask 类为例

任务类:

public class ForkJoinDemo extends RecursiveTask<Long> {
    long start;
    long end;

    //临界值
    long temp = 10000L;

    public ForkJoinDemo(long start, long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        if ((end-start)<temp){
            long sum = 0L;
            for (long i = start; i < end; i++) {
                sum += i;
            }
            return sum;
        } else {
            long middle = (start + end)/2;
            //将当前task拆分 创建子任务(类似于递归 若拆分后end-start仍然大于temp 会在新建的task内部继续拆分)
            ForkJoinDemo task01 = new ForkJoinDemo(start, middle);
            ForkJoinDemo task02 = new ForkJoinDemo(middle, end);
            //将新创建的子任务放入当前线程的workQueue队列中
            task01.fork();
            task02.fork();

            //返回子任务及其子任务的执行结果的汇总
            return task01.join() + task02.join();
        }
    }
}

创建并执行任务:

将Fork/Join与单线程执行及并行流式计算作对比

public class TestForkJoin {
    public static void main(String[] args) {
        test01();
        test02();
        test03();
    }

    public static void test01(){
        long sum = 0L;
        long start = System.nanoTime();
        for (long i = 0; i < 1000000000; i++) {
            sum += i;
        }
        long end = System.nanoTime();
        System.out.println("for");
        System.out.println("time: " + (end-start));
        System.out.println("result: " + sum);
    }

    public static void test02(){
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> forkJoinTask = new ForkJoinDemo(1,1000000000);

        long start = System.nanoTime();
        Long invoke = forkJoinPool.invoke(forkJoinTask);
        long end = System.nanoTime();

        System.out.println("ForkJoin");
        System.out.println("time: " + (end-start));
        System.out.println("result: " + invoke);
    }

    public static void test03(){
        long start = System.nanoTime();
        //range() [)    rangeClose() []
        //parallel() 返回等效的并行流 可能返回流本身(流本身即为并行流或基础流的状态被修改为并行)
        //long reduce(long identity, LongBinaryOperator op); 根据指定计算模型将Stream中的数据进行计算并返回结果(参数:计算的初始值 计算模型)计算的初始值不为0会发生线程安全问题
        //long sum = LongStream.range(0, 1000000000).parallel().reduce(0, Long::sum);
        long sum = LongStream.range(0, 1000000000).parallel().sum();//与上行注释中的代码等价(sum()内部调用reduce(0, Long::sum);)
        long end = System.nanoTime();

        System.out.println("Stream");
        System.out.println("time: " + (end-start));
        System.out.println("result: " + sum);
    }
}

运行效果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值