Fork/Join中的调用原理

错误程序

public class Calculator extends RecursiveTask {
	 
    private static final int THRESHOLD = 100;
    private int start;
    private int end;
 
    public Calculator(int start, int end) {
        this.start = start;
        this.end = end;
    }
 
    @Override
    protected Integer compute() {
        int sum = 0;
        if((start - end) < THRESHOLD){
            for(int i = start; i< end;i++){
                sum += i;
            }
        }else{
            int middle = (start + end) /2;
            Calculator left = new Calculator(start, middle);
            Calculator right = new Calculator(middle + 1, end);
            left.fork();
            right.fork();
 
            sum = left.join() + right.join();
        }
        return sum;
    }
 
}

(1)fork

                1.基本作用

                   将任务放入任务队列队尾

                2.源码

    public final ForkJoinTask fork() {
        ((ForkJoinWorkerThread) Thread.currentThread())
            .pushTask(this);
        return this;
    }
                 该方法属于:ForkJoinTask,也就是说this指向任务本身,将任务添加到任务队列末尾

                 因为运行ForkJoinTask的线程就是ForkJoinWorkerThread,所以可以调用其中的pushTask方法

   /**
     * Pushes a task. Call only from this thread.
     *
     * @param t the task. Caller must ensure non-null.
     */
    final void pushTask(ForkJoinTask t) {
        ForkJoinTask[] q; int s, m;
        if ((q = queue) != null) {    // ignore if queue removed
            long u = (((s = queueTop) & (m = q.length - 1)) << ASHIFT) + ABASE;
            UNSAFE.putOrderedObject(q, u, t);
            queueTop = s + 1;         // or use putOrderedInt
            if ((s -= queueBase) <= 2)
                pool.signalWork();
            else if (s == m)    //扩容用
                growQueue();
        }
    }
            其中有个成员变量queue,用来存储和本线程对应的任务队列

            定义:

    ForkJoinTask[] queue;

           初始化:

    protected void onStart() {
        queue = new ForkJoinTask[INITIAL_QUEUE_CAPACITY];
        ....
    }
       接着继续看pushTask

            if ((s -= queueBase) <= 2)
                pool.signalWork();
       就是说当任务队列中只有一个任务时,从线程池中调用线程执行


(2)join

                1.基本作用

                   阻塞当前线程,直到本任务执行完毕(相当于ForkJoinTask版的Thread.join

                2.源码

    public final V join() {
        if (doJoin() != NORMAL)
            return reportResult();
        else
            return getRawResult();
    }
                主要是为了看返回值,接着看doJoin

    private int doJoin() {
        Thread t; ForkJoinWorkerThread w; int s; boolean completed;
        if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) {
            if ((s = status) < 0)
                return s;
            if ((w = (ForkJoinWorkerThread)t).unpushTask(this)) {
                try {
                    completed = exec();
                } catch (Throwable rex) {
                    return setExceptionalCompletion(rex);
                }
                if (completed)
                    return setCompletion(NORMAL);
            }
            return w.joinTask(this);
        }
        else
            return externalAwaitDone();
    }
                这里先通过status判断状态

    volatile int status; // accessed directly by pool and workers
    private static final int NORMAL      = -1;
    private static final int CANCELLED   = -2;
    private static final int EXCEPTIONAL = -3;
    private static final int SIGNAL      =  1;
             也就是说<0的是已经执行完毕的, 这对于窃取非常重要

             然后通过unpushTask取任务然后执行,看下unpushTask

    final boolean unpushTask(ForkJoinTask t) {
        ForkJoinTask[] q;
        int s;
        if ((q = queue) != null && (s = queueTop) != queueBase &&
            UNSAFE.compareAndSwapObject
            (q, (((q.length - 1) & --s) << ASHIFT) + ABASE, t, null)) {
            queueTop = s; // or putOrderedInt
            return true;
        }
        return false;
    }
            注意其中的 --s,也就是 说是从队列尾取的任务


(3)分析

            left.fork();
            right.fork();
 
            sum = left.join() + right.join();
             由上面可知,这段代码的意思就是:

             1.把left放入任务队列,right放入任务队列。此时right在队尾,left在倒数第二个

             2.left.join(),因为当前right在队尾,所以说取不出来!!!因而线程就会阻塞


            正确的是调用invokeAll,看下invokeAll

    public static void invokeAll(ForkJoinTask t1, ForkJoinTask t2) {
        t2.fork();
        t1.invoke();
        t2.join();
    }
           也就是说先把t2加入队列中,然后直接执行t1,接着对t2执行join

           对应上例就是:把right放入队列尾部,然后left开始执行,最后对right进行join,直到left执行完毕才能执行(当然right可能被其他线程窃取,这样通过状态判断就直接返回了)

            更一般的

    public static void invokeAll(ForkJoinTask... tasks) {
        Throwable ex = null;
        int last = tasks.length - 1;
        for (int i = last; i >= 0; --i) {
            ForkJoinTask t = tasks[i];
            if (t == null) {
                if (ex == null)
                    ex = new NullPointerException();
            }
            else if (i != 0)
                t.fork();
            else if (t.doInvoke() < NORMAL && ex == null)
                ex = t.getException();
        }
        for (int i = 1; i <= last; ++i) {
            ForkJoinTask t = tasks[i];
            if (t != null) {
                if (ex != null)
                    t.cancel(false);
                else if (t.doJoin() < NORMAL && ex == null)
                    ex = t.getException();
            }
        }
        if (ex != null)
            UNSAFE.throwException(ex);
    }
         可以看到 i != 0 才进行fork,也就是说队列中第一个都是直接执行的,其他的以此fork,fork之后的任务之后进行join



(4)invoke

                1.基本作用

                   直接执行任务

                2.源码

    public final V invoke() {
        if (doInvoke() != NORMAL)
            return reportResult();
        else
            return getRawResult();
    }
                  再看下doInvoke

    private int doInvoke() {
        int s; boolean completed;
        if ((s = status) < 0)
            return s;
        try {
            completed = exec();
        } catch (Throwable rex) {
            return setExceptionalCompletion(rex);
        }
        if (completed)
            return setCompletion(NORMAL);
        else
            return doJoin();
    }
            其中的exec()在RecursiveTask中

    protected final boolean exec() {
        result = compute();
        return true;
    }
         在RecursiveAction中
    protected final boolean exec() {
        compute();
        return true;
    }
       也就是说直接执行了



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