ForkAndJoin框架概念

     ForkJoinPool是ExecutorService接口的实现,它专为可以递归分解成小块的工作而设计。

fork/join框架将任务分配给线程池中的工作线程,充分利用多处理器的优势。

     使用fork/join框架的第一步是编写一部分工作的代码。类似的伪代码如下:

如果(当前工作部分足够小)
    直接做这项工作
其他
   把当前工作分成两部分
   调用这两个部分并等待结果

 

     将此代码包装在ForkJoinTask子类中,通常是RecursiveTask(可以返回执行结果)或者RecursiveAction。

      假如有一个从1累加到100的需求,使用forkAndJoin框架可以如下实现:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

public class ForkAndJoin {

    //获得执行ForkAndJoin任务的线程池
    private static final ForkJoinPool forkJoinPool = (ForkJoinPool) Executors.newWorkStealingPool();


    public static void main(String args[]) throws ExecutionException, InterruptedException {
        List<Integer> list = new ArrayList<>();
        for (int i = 1; i < 101; i++) {
            list.add(i);
        }
        ForkAndJoinRequest request = new ForkAndJoinRequest(0, list.size() - 1, list);
        forkJoinPool.submit(request);
        System.out.println(request.get());
    }


}

//定义request继承RecursiveTask,并实现compute方法
class ForkAndJoinRequest extends RecursiveTask<Integer> {

    private int start;
    private int end;
    private List<Integer> list;

    public ForkAndJoinRequest(int start, int end, List<Integer> list) {
        this.start = start;
        this.end = end;
        this.list = list;
    }

    @Override
    protected Integer compute() {
        int count = end - start;
        if (count <= 25) { //如果需要累加的数量小于等于25,则直接执行
            int result = 0;
            for (int i = start; i <= end; i++) {
                result += i;
            }
            return result;
        } else { //否则fork出其他request
            int mid = (start + end) / 2;
            ForkAndJoinRequest request1 = new ForkAndJoinRequest(start, mid, list);
            request1.fork(); //调用fork方法将自身放入等待队列中等待执行
            ForkAndJoinRequest request2 = new ForkAndJoinRequest(mid + 1, end, list);
            request2.fork();

            //等待执行结果
            return request1.join() + request2.join();

        }
    }
}

  

ForkJoin实现思路:

  每个Worker线程都维护一个任务队列,即ForkJoinWorkerThread中的任务队列

  任务队列是双向队列,这样可以同时实现LIFO和FIFO

  子任务会被加入到原先任务所在Worker线程的任务队列

   Work线程使用LIFO的方法取出任务,后进队列的任务先取出来(子任务需要先执行)

   当任务队列为空,会随机从其他Worker的队列中拿走一个任务执行(工作窃取)

   如果一个Worker线程遇到了join操作,而这时正在处理其他任务,会等到这个任务执行结束,否则立刻返回

   如果一个Worker线程窃取任务失败,它会调用yield或者sleep方法休息一会再尝试。如果所有线程都是等待状态,那么此线程业务

阻塞直到新任务的到来

   

 

ForkJoinPool适用情况:

   使用尽可能少的线程池--在大多数情况下,最好的决定是为每个应用程序或者系统使用一个线程池。

   如果不需要特定调整,请使用默认的公共线程池。ForkJoinPool默认会产生CPU个数的线程

   使用合理的阈值将ForkJoinTask拆分为子任务

   避免在ForkJoinTask中出现任何阻塞。所以文件操作,http接口调用等场景不建议使用

   适合数据处理、结果汇总、统计等场景

   java8实例:java.util.Arrays类用于其parallelSort()方法

posted @ 2019-01-13 19:56  gluawwa  阅读(1171)  评论(0编辑  收藏  举报