Java8 RecursiveAction / RecursiveTask / CountedCompleter 源码解析

本文深入解析Java8的ForkJoinPool中的RecursiveAction、RecursiveTask和CountedCompleter。重点讨论它们在并行计算中的应用场景、实现细节以及性能优化。通过具体例子展示了如何使用RecursiveAction进行普通递归和非递归实现,RecursiveTask的使用,以及CountedCompleter在并发任务中的应用,包括BulkTask、ForEachMappingTask、ReduceEntriesTask和SearchEntriesTask等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 目录

一、RecursiveAction

1、普通的递归实现

2、非递归实现

二、RecursiveTask

三、CountedCompleter

1、定义

2、BulkTask

3、ForEachMappingTask

4、ReduceEntriesTask

5、SearchEntriesTask

6、complete / tryComplete / helpComplete / internalPropagateException


RecursiveAction / RecursiveTask / CountedCompleter 这3个类是ForkJoinTask对外的三个子类,因为ForkJoinTask本身不是直接使用的,所以先了解其子类的具体使用方式和实现细节,后面的博客再探讨ForkJoinTask和ForkJoinPool的实现。

一、RecursiveAction

1、普通的递归实现

     RecursiveAction表示一个没有返回值的递归执行的任务,该类继承自ForkJoinTask,该类的定义如下:

其中我们能改写的只有compute方法,实现具体的计算逻辑,另外三个都是final方法,无法改写。 测试用例如下:

class IntAdder extends RecursiveAction {

    private static final long serialVersionUID = 5349972192067990608L;
    private final List<Integer> datas;
    private final int threshold;
    private long result;

    public IntAdder(List<Integer> datas, int level) {
        super();
        this.datas = datas;
        this.threshold = level;
    }

    public static int sum(List<Integer> list) {
        int sum = 0;
        for (Integer i : list) {
            sum += i;
        }
        return sum;
    }

   @Override
    protected void compute() {
        System.out
                .println(Thread.currentThread().getName() + " start to work,data size->"+datas.size());
        if (datas.size() < threshold) {
            result = sum(datas);
        } else {
            int toIndex = datas.size() / 2;
            IntAdder left = new IntAdder(datas.subList(0,
                    toIndex), threshold);
            IntAdder right = new IntAdder(datas.subList(
                    toIndex, datas.size()), threshold);
            System.out.println(Thread.currentThread().getName() + " start wait,data size->"+datas.size());
            //阻塞当前线程,等待两个子任务执行完成
            //底层逻辑是立即执行left,将right提交到任务队列,left执行完成后,然后等待right执行完成
            invokeAll(left,right);
            System.out.println(Thread.currentThread().getName() + " end wait,data size->"+datas.size());
            result = left.result+right.result;
        }
    }


    public long getResult() {
        return result;
    }

    private static List<Integer> createTestDatas(int count) {
        List<Integer> list = new ArrayList<Integer>();
        Random random = new Random();
        for (int i = 0; i < count; i++) {
            list.add(random.nextInt(10000));
        }
        return list;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        int count = 100000;
        int level = count / 5;
        List<Integer> datas = createTestDatas(count);
        long before = System.currentTimeMillis();
        long sum = 0;
        for (Integer i : datas) {
            sum += i;
        }
        System.out.println("result->" + sum + ",time->" + (System.currentTimeMillis() - before));
        IntAdder t = new IntAdder(datas, level);
        ForkJoinPool pool = new ForkJoinPool(Runtime.getRuntime()
                .availableProcessors() * 4);
        before = System.currentTimeMillis();
        //会阻塞当前线程,直到任务执行完成
        pool.invoke(t);
        System.out.println("result->" + t.getResult() + ",time->"
                + (System.currentTimeMillis() - before));
    }
}

上述测试用例的输出如下:

result->498313847,time->4
//worker-9是第一个执行任务的线程,先执行left,不断递归,直到可以不用切分出子任务,计算完left,等待right执行完成,然后逐级向上返回
ForkJoinPool-1-worker-9 start to work,data size->100000
ForkJoinPool-1-worker-9 start wait,data size->100000
ForkJoinPool-1-worker-9 start to work,data size->50000
ForkJoinPool-1-worker-9 start wait,data size->50000
ForkJoinPool-1-worker-9 start to work,data size->25000
ForkJoinPool-1-worker-9 start wait,data size->25000
ForkJoinPool-1-worker-9 start to work,data size->12500
ForkJoinPool-1-worker-9 end wait,data size->25000
ForkJoinPool-1-worker-9 end wait,data size->50000
ForkJoinPool-1-worker-9 end wait,data size->100000

//worker-2是第二个执行任务的线程,执行worker-9切分出来的right任务,同样的会不断递归,逻辑同上
ForkJoinPool-1-worker-2 start to work,data size->50000
ForkJoinPool-1-worker-2 start wait,data size->50000
ForkJoinPool-1-worker-2 start to work,data size->25000
ForkJoinPool-1-worker-2 start wait,data size->25000
ForkJoinPool-1-worker-2 start to work,data size->12500
ForkJoinPool-1-worker-2 start to work,data size->12500
ForkJoinPool-1-worker-2 end wait,data size->25000
ForkJoinPool-1-worker-2 end wait,data size->50000

//worker-11和worker-4是第三个和第四个线程
ForkJoinPool-1-worker-11 start to work,data size->25000
ForkJoinPool-1-worker-11 start wait,data size->25000
ForkJoinPool-1-worker-11 start to work,data size->12500
ForkJoinPool-1-worker-11 end wait,data size->25000

ForkJoinPool-1-worker-6 start to work,data size->12500
ForkJoinPool-1-worker-6 start to work,data size->12500

ForkJoinPool-1-worker-4 start to work,data size->25000
ForkJoinPool-1-worker-4 start wait,data size->25000
ForkJoinPool-1-worker-4 start to work,data size->12500
ForkJoinPool-1-worker-4 end wait,data size->25000

ForkJoinPool-1-worker-10 start to work,data size->12500

result->498313847,time->7

 从耗时看,因为存在提交任务到线程的递归fork动作和让线程休眠等待的join操作,在任务量较的情形下使用ForkJoinPool的性能比单线程要差一半。

2、非递归实现

    还有一种用法可以显著改进上述示例中的递归fork问题,如下:

package synchronizedTest;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;

class IntAdder2 extends RecursiveAction {

    private static final long serialVersionUID = 5349972192067990608L;
    private final List<Integer> datas;
    private final int threshold;
    private long result;
    private IntAdder2 next;

    public IntAdder2(List<Integer> datas, int level, IntAdder2 next) {
        super();
        this.datas = datas;
        this.threshold = level;
        this.next=next;
    }

    public static int sum(List<Integer> list) {
        int sum = 0;
        for (Integer i : list) {
            sum += i;
        }
        return sum;
    }

    @Override
    protected void compute() {
        System.out
                .println(Thread.currentThread().getName() + " start to work,data size->"+datas.size());
        int start = 0, size = datas.size();
        IntAdder2 right = null;
        while (size > threshold) {
            right = new IntAdder2(datas.subList(start, start + threshold), threshold,right);
            //创建一个子任务,提交到任务队列
            right.fork();
            start += threshold;
            size -= threshold;
        }
        long sum = sum(datas.subList(start, datas.size()));
        while (right!=null) {
            if (right.tryUnfork()) { //尝试将该任务从任务队列中移除,由当前线程执行
                sum += sum(right.datas);
            } else {
                right.join(); //该任务已经在执行了,则等待任务执行完成
                sum += right.result;
            }
            right=right.next;
        }
        result=sum;
    }

    public long getResult() {
        return result;
    }

    private static List<Integer> createTestDatas(int count) {
        List<Integer> list = new ArrayList<Integer>();
        Random random = new Random();
        for (int i = 0; i < count; i++) {
            list.add(random.nextInt(10000));
        }
        return list;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值