Fork/Join框架简介与基本运用

ForkJoin框架是Java 7 提供的一个分治思想的基本框架,即把一个大任务分割成若干个小任务,最终汇总每一个任务结果后得到大任务结果。通过查阅API发现,ForkJoinPool继承AbstractExecutorService,实现了ExecutorExecutorServiceForkJoinPool用来实现工作窃取 算法。

一、ForkJoinTask及其子类

通过查看API发现,ForkJoinTask是一个实现了Future接口的抽象类,在实际开发中,不需要继承ForkJoinTask,而是重写其子类的compute方法即可,在compute方法里实现任务的切分和结果的合并。
常用子类为:RecursiveAction (无返回值), RecursiveTask (自定义泛型返回值)

二、fork\join 源码解析

fork()方法源码解析:

 public final ForkJoinTask<V> fork() {
        Thread t;
        if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
            ((ForkJoinWorkerThread)t).workQueue.push(this);
        else
            ForkJoinPool.common.externalPush(this);
        return this;
    }

此方法将任务放入ForkJoinWorkerThread线程的工作队列,异步执行,立即返回。
ForkJoinPoolsignalWork方法唤醒或创建一个线程来执行这个任务的compute方法,如果还是大于切分的阀值,则会继续切分然后调用fork添加任务直到任务不再可切分。
join() 方法源码解析:

public final V join() {
        int s;
        if ((s = doJoin() & DONE_MASK) != NORMAL)
            reportException(s);
        return getRawResult();
    }

阻塞当前线程,直到对应的子任务完成运行并返回执行结果。或者,如果这个子任务存在于当前线程的任务等待队列中,则取出这个子任务进行递归执行。

三、实例

以一个文件遍历为例,进行示范:

public class ForkJoinTest extends RecursiveAction {
    private String FilePath;
    private ForkJoinTest(String FilePath){
        this.FilePath = FilePath;
    }
    @Override
    protected void compute() {
        List<ForkJoinTest> forkJoinTests = new LinkedList<>();
        File Dir = new File(FilePath);
        if(!Dir.exists()){
            System.out.println("文件夹不存在");
        }else if(Dir.isFile()){
            System.out.println(Dir.getPath()+Dir.getName());
        }else if(Dir.isDirectory()){
            for (File file : Dir.listFiles()) {
                forkJoinTests.add(new ForkJoinTest(file.getPath()));
            }
        }
        if(!forkJoinTests.isEmpty()){
            for(ForkJoinTest forkJoinTest : invokeAll(forkJoinTests)){
                forkJoinTest.join();
            }
        }
    }

        public static void main(String[] args) {
            ForkJoinPool forkJoinPool = new ForkJoinPool();
            ForkJoinTest forkJoinTest = new ForkJoinTest("F:\\File");
            long time_start = System.currentTimeMillis();
            forkJoinPool.execute(forkJoinTest);
            forkJoinTest.join();
            long time_end = System.currentTimeMillis();
            System.out.println(time_end-time_start);
        }
}

public static <T extends ForkJoinTask<?>> Collection<T> invokeAll(Collection<T> tasks):指定集合中的所有任务,当每个任务保持isDone或遇到一个(未检查)异常时返回,在这种情况下,异常被重新引导。 如果有多个任务遇到异常,则该方法会抛出任何一个异常。 如果任何一个任务遇到异常,其他任务可能被取消。 但是,单独任务的执行状态在特殊返回时不能得到保证。

在fork/join模式中,我们在子任务中常常使用fork方法来让子任务采取异步方法执行,但是这不是高效的实现方法,尤其是对于forkjoinPool在线程有限的情况下,子任务直接使用fork方法执行时间比使用invokeAll执行时间要长。
下面分析一下原理:对于fork/join模式,假如pool里面线程数量是固定的,那么调用子任务的fork方法相当于A先分工给B,然后A当监工不干活,B去完成A交代的任务。所以上面的模式相当于浪费了一个线程。那么如果使用invokeAll相当于A分工给B后,A和B都去完成工作。这样缩短了执行的时间。
摘自:https://blog.csdn.net/cxl0921/article/details/76460909

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值