Fork/Join中的调用原理

来源:互联网 发布:maya软件价格 编辑:程序博客网 时间:2024/05/23 01:59

错误程序

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<V> 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;    }
       也就是说直接执行了



0 0
原创粉丝点击