Fork/Join框架

 什么是Fork/Join

Fork/Join框架是一个实现了ExecutorService接口的多线程处理器,它专为那些可以通过递归分解成更细小的任务而设计,最大化的利用多核处理器来提高应用程序的性能

Fork/Join框架的工作大致流程为:

solve(任务):
    if(任务已经划分到足够小):
        顺序执行任务
    else:
        for(划分任务得到子任务)
            solve(子任务)
        结合所有子任务的结果到上一层循环
        return 最终结合的结果

Fork/Join框架_第1张图片

 

工作窃取算法

Fork/Join框架在执行任务时使用了工作窃取算法,所谓的工作窃取算法是指:多线程执行不同任务队列的过程中,某个线程执行完自己队列的任务后从其他线程的任务队列里窃取任务来执行。

Fork/Join框架_第2张图片

当一个线程窃取另一个线程的时候,为了减少两个任务线程之间的竞争,我们通常使用双端队列来存储任务。被窃取的任务线程都从双端队列的头部拿任务执行,而窃取其他任务的线程从双端队列的尾部执行任务。

当一个线程窃取任务时要是没有其他可用的任务了,它会进入阻塞状态以等待再次“工作”。

 

Fork/Join框架的设计

Fork/Join使用两个类来完成分割任务和执行任务并合并结果两件事情:

ForkJoinTask:首先要创建一个ForkJoinTask任务,它提供任务中执行fork()和join()操作。通常我们需要继承它的子类,这两个子类都有执行主要计算的方法compute()

  • RecursiveAction:用于没有返回结果的任务。
  • RecursiveTask:用于有返回结果的任务。

ForkJoinPool:ForkJoinTask需要通过ForkJoinPool来执行。任务分割的子任务会添加到当前工作线程维护的双端队列中,进入队列的头部。当一个工作线程的队列里暂时没有任务时,它会随机从其他工作线程的队列的尾部获取一个任务。
 

 

ForkJoinTask

fork()方法

ForkJoinTask的fork()方法会使用线程池中的空闲线程异步提交任务。

    public final ForkJoinTask fork() {
        Thread t;
//判断当前线程t是否是执行ForkJoinTask的专有线程ForkJoinWorkerThread,若是则把任务push到当前线程
//负责的队列里
        if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
            ((ForkJoinWorkerThread)t).workQueue.push(this);
        else        //若不是则提交任务到默认的common线程池里。
            ForkJoinPool.common.externalPush(this);
        return this;
    }

join()方法

join()方法则等待处理任务的线程处理完毕后,返回结果。

public final V join() {
    int s;
    // doJoin()方法来获取当前任务的执行状态
    if ((s = doJoin() & DONE_MASK) != NORMAL)
        // 任务异常,抛出异常
        reportException(s);
    // 任务正常完成,获取返回值
    return getRawResult();
}

/**
 * doJoin()方法用来返回当前任务的执行状态
 **/
private int doJoin() {
    int s; Thread t; ForkJoin若WorkerThread wt; ForkJoinPool.WorkQueue w;
    // 先判断任务是否执行完毕,执行完毕直接返回结果(执行状态)
    return (s = status) < 0 ? s :
    // 如果没有执行完毕,先判断是否是ForkJoinWorkThread线程
    ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
        // 如果是,先判断任务是否处于工作队列顶端(意味着下一个就执行它)
        // tryUnpush()方法判断任务是否处于当前工作队列顶端,是返回true
        // doExec()方法执行任务
        (w = (wt = (ForkJoinWorkerThread)t).workQueue).
        // 如果是处于顶端并且任务执行完毕,返回结果
        tryUnpush(this) && (s = doExec()) < 0 ? s :
        // 如果不在顶端或者在顶端却没未执行完毕,那就调用awitJoin()执行任务
        // awaitJoin():使用自旋使任务执行完成,返回结果
        wt.pool.awaitJoin(w, this, 0L) :
    // 如果不是ForkJoinWorkThread线程,执行externalAwaitDone()返回任务结果
    externalAwaitDone();
}

执行流程为:

Fork/Join框架_第3张图片

 

ForkJoinPool

ForkJoinPool是用于执行ForkJoinTask任务的执行(线程)池,它管理执行池中的线程和任务队列,此外,执行池是否还接受任务,显示线程的运行状态也是在这里处理。

public class ForkJoinPool extends AbstractExecutorService {
    // 存放ForkJoinTask任务队列,每个工作线程都维护着一个工作队列。
    volatile WorkQueue[] workQueues;   

    // 线程的运行状态,负数表示SHUTDOWN状态
    volatile int runState;  

    // 创建ForkJoinWorkerThread的默认工厂,可以通过构造函数重写
    public static final ForkJoinWorkerThreadFactory defaultForkJoinWorkerThreadFactory;

    // 公用的线程池,其运行状态不受shutdown()和shutdownNow()的影响
    static final ForkJoinPool common;

    // 私有构造方法,没有任何安全检查和参数校验,由makeCommonPool直接调用
    // 其他构造方法都是源自于此方法
    // parallelism: 并行度,
    // 默认调用java.lang.Runtime.availableProcessors() 方法返回可用处理器的数量
    private ForkJoinPool(int parallelism,
                         ForkJoinWorkerThreadFactory factory, // 工作线程工厂
                         UncaughtExceptionHandler handler, // 拒绝任务的handler
                         int mode, // 同步模式
                         String workerNamePrefix) { // 线程名prefix
        this.workerNamePrefix = workerNamePrefix;
        this.factory = factory;
        this.ueh = handler;
        this.config = (parallelism & SMASK) | mode;
        long np = (long)(-parallelism); // offset ctl counts
        this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
    }

}

 

Fork/Join框架使用示例

(1)通过ForkJoinTask来计算1~10的累加和,将任务最终划分成多个长度小于等于2的子任务,然后再计算。

public class Test {
   public static class CountTask extends RecursiveTask {
       private static final int MAX_CanCompute = 2;
       private int start, end;


       public CountTask(int start, int end) {
           this.start = start;
           this.end = end;
       }

       @Override
       protected Integer compute() {
           int curSum = 0;
           boolean curCanCompute = (end - start) <= MAX_CanCompute;
           if(curCanCompute) {
               for(int i = start; i <= end; i++) {
                   curSum += i;
               }
           }else {
               int mid = (start + end) / 2;
               CountTask leftTask = new CountTask(start, mid);
               CountTask rightTask = new CountTask(mid + 1, end);
               leftTask.fork();         rightTask.fork();
               curSum += leftTask.join() + rightTask.join();
           }
           return curSum;
       }
   }
    public static void main(String[] args) {
       ForkJoinPool pool = new ForkJoinPool();
       CountTask task = new CountTask(1, 10);
       Future res = pool.submit(task);
        try {
            System.out.println(res.get());
        } catch (InterruptedException e) {
        } catch (ExecutionException e) {
        }
    }
}

(2)计算斐波那契数列

public class Test {
   public static class CountTask extends RecursiveTask {
       private  int MAX_CanCompute;

       public CountTask(int MAX_CanCompute) {
           this.MAX_CanCompute = MAX_CanCompute;
       }

       @Override
       protected Integer compute() {
           if(MAX_CanCompute <= 1) {
               return MAX_CanCompute;
           }else {
               CountTask f1 = new CountTask(MAX_CanCompute - 1);
               CountTask f2 = new CountTask(MAX_CanCompute - 2);
               f1.fork();
               f2.fork();
               return f1.join() + f2.join();
           }
       }
   }
    public static void main(String[] args) {
       ForkJoinPool pool = new ForkJoinPool();
       CountTask task = new CountTask(33);
       Future res = pool.submit(task);
        try {
            System.out.println(res.get());
        } catch (InterruptedException e) {
        } catch (ExecutionException e) {
        }
    }
}

 

 


参考资料

http://concurrent.redspider.group/article/03/18.html

你可能感兴趣的:(多线程)