ForkJoin全解3:CountedCompleter

1、使用示例

import java.util.concurrent.CountedCompleter;
import java.util.concurrent.atomic.AtomicInteger;
public class CountedCompleterDemo {
    interface Applier<E>{
        void apply(E e);
    }

    static class ForEach<E> extends CountedCompleter<Void> {
        public static <E> void forEach(E[] array, Applier<E> op) {
            new ForEach<>(null, array, op, 0, array.length).invoke();
        }

        final E[] array;
        final Applier<E> op;
        final int lo, hi;
	//p是父任務
        ForEach(CountedCompleter<?> p, E[] array, Applier<E> op, int lo, int hi) {
            super(p);
            this.array = array;
            this.op = op;
            this.lo = lo;
            this.hi = hi;
        }

        public void compute() { 
            if (hi - lo >= 2) {
                int mid = (lo + hi) >>> 1;
                setPendingCount(2); // must set pending count before fork
                new ForEach(this, array, op, mid, hi).fork(); // right child
                new ForEach(this, array, op, lo, mid).fork(); // left child
            }
            else if (hi > lo)
                op.apply(array[lo]);
            tryComplete();
        }
    }
    public static void main(String[] args) throws Exception{
        AtomicInteger sum=new AtomicInteger();
        ForEach.forEach(new Integer[]{1,2,3,4,5,6},(element)->{
            sum.addAndGet(element) ;
        });
        System.out.println("相加之和:"+sum.get());
    }
}

2、countedCompleter是怎麼開始執行的

ForkJoinPool會調用ForkJoinTask的doExec來執行任務,doExec方法如下:

final int doExec() {
    int s; boolean completed;
    if ((s = status) >= 0) {
        try {
            completed = exec();
        } catch (Throwable rex) {
            return setExceptionalCompletion(rex);
        }
        if (completed)
            s = setCompletion(NORMAL);
    }
    return s;
}

這裏通過調用exec方法來執行任務。exec是抽象方法,CountedCompleter中對exec的實現如下:

protected final boolean exec() {

     compute();

     return false;

}

這樣compute的邏輯就是任務的核心計算邏輯,會最終被pool調用。

3、countedCompleter運行過程

在示例中,我們把最開始new出來的任務稱爲根任務,根任務在執行過程中,又fork出2個子任務,子任務可能會fork出自己的子任務,一層層fork下去後,最終一定有一個任務是不會再fork的,這個任務稱爲葉子任務。整個任務體系就是一顆完全二叉樹。

整個執行過程,最核心的屬性是:

pending:表示等待執行的任務

completer:表示任務的上級,即父任務

核心方法是tryComplete:

public final void tryComplete() {
    CountedCompleter<?> a = this, s = a;
    for (int c;;) {
        if ((c = a.pending) == 0) {
            a.onCompletion(s);
            if ((a = (s = a).completer) == null) {
                s.quietlyComplete();
                return;
            }
        }
        else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
            return;
    }
}

整個執行過程如下圖所示:

 

看一下invoke方法:

private int doInvoke() {
        int s; Thread t; ForkJoinWorkerThread wt;
        return (s = doExec()) < 0 ? s :
            ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
            (wt = (ForkJoinWorkerThread)t).pool.
            awaitJoin(wt.workQueue, this, 0L) :
            externalAwaitDone();
}

這裏根任務的doExec會被執行,即根任務被執行,之後就會join等待。所以根任務會等待,而子任務不會等待。而無論是pool的awaitJoin還是task的externalAwaitDone,都是通過helpComplete來幫助子任務執行

另外,這裏所說的子任務不會等待,僅僅是以本例作爲例子的特例情況,如果子任務中使用了join等方法,子任務也是會等待的。所以把根任務的概念推廣一下,凡是需要等待的都稱爲根任務,不執行fork的稱爲葉子任務,不需等待,但是會fork的稱爲樹幹任務

4、helpComplete

核心實現思想:worker先嚐試從自身隊列中獲取task或者task的子任務,若能獲取到則執行,否則從其他隊列竊取task或者task的子任務,若能竊取到則執行。不斷如此,直到task完成。

源碼如下:

/** 
*
 * @param w 執行子任務的worker
* @param task 等待的任務
 * @param maxTasks表示helpComplete最多執行多少個子任務,爲0表示一直執行,直到根任務完成
 * @return task執行狀態
 */
final int helpComplete(WorkQueue w, CountedCompleter<?> task,int maxTasks) {
WorkQueue[] ws; int s = 0, m;
    if ((ws = workQueues) != null && (m = ws.length - 1) >= 0 &&
        task != null && w != null) {
        int mode = w.config;                 // for popCC
        int r = w.hint ^ w.top;              // arbitrary seed for origin
        int origin = r & m;                  // first queue to scan
        int h = 1;                           // 1:ran, >1:contended, <0:hash
        for (int k = origin, oldSum = 0, checkSum = 0;;) {
            CountedCompleter<?> p; WorkQueue q;
            if ((s = task.status) < 0)
                break;
	//嘗試從當前worker的工作隊列中獲取task或者其子任務來執行
            if (h == 1 && (p = w.popCC(task, mode)) != null) {
                p.doExec();                  // run local task
                if (maxTasks != 0 && --maxTasks == 0)
                    break;
                origin = k;                  // reset
                oldSum = checkSum = 0;
            }
            else {                           // poll other queues
                if ((q = ws[k]) == null)
                    h = 0;
	//小於0表示q爲空,或者q有非CountedCompleter的任務
                else if ((h = q.pollAndExecCC(task)) < 0)
                    checkSum += h;
                if (h > 0) {
		//如果h==1,說明上面q.pollAndExecCC(task)執行成功了一個task或者task的子任務
                    if (h == 1 && maxTasks != 0 && --maxTasks == 0)
                        break;
                    r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // xorshift
                    origin = k = r & m;      // move and restart
                    oldSum = checkSum = 0;
                }
		//如果(k = (k + 1) & m) == origin成立,說明整個workQueues數組被遍歷了一遍
                else if ((k = (k + 1) & m) == origin) {
		//要滿足oldSum == (oldSum = checkSum),h=q.pollAndExecCC(task)的結果必須≥0
		//而這個if是在else if內,所以h=0,即要滿足這個條件,需要workQueues數組的所有
		//隊列都爲null
                    if (oldSum == (oldSum = checkSum))
                        break;
                    checkSum = 0;
                }
            }
        }
    }
    return s;
}

5、popCC

/** 
* 獲取task的子任務
* @param task 父任務
* @param mode worker的mode
* @return task或task的子任務
 */
final CountedCompleter<?> popCC(CountedCompleter<?> task, int mode) {
int s; ForkJoinTask<?>[] a; Object o;
    if (base - (s = top) < 0 && (a = array) != null) {
        long j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE;
        if ((o = U.getObjectVolatile(a, j)) != null &&
            (o instanceof CountedCompleter)) {
            CountedCompleter<?> t = (CountedCompleter<?>)o;
            for (CountedCompleter<?> r = t;;) {
                if (r == task) {
		//如果是公共隊列,可能有多個worker更改它的top,所以需要加鎖。非公共隊列僅能
		//該隊列的worker操作它的top
                    if (mode < 0) { // must lock
                        if (U.compareAndSwapInt(this, QLOCK, 0, 1)) {
                            if (top == s && array == a &&
                                U.compareAndSwapObject(a, j, t, null)) {
                                U.putOrderedInt(this, QTOP, s - 1);
                                U.putOrderedInt(this, QLOCK, 0);
                                return t;
                            }
                            U.compareAndSwapInt(this, QLOCK, 1, 0);
                        }
                    }
                    else if (U.compareAndSwapObject(a, j, t, null)) {
                        U.putOrderedInt(this, QTOP, s - 1);
                        return t;
                    }
                    break;
                }
                else if ((r = r.completer) == null) // try parent
                    break;
            }
        }
    }
    return null;
}

6、pollAndExecCC

/** 
* 從base位置開始獲取任務,如果是task的子任務或者就是task,則執行之
* @param task 父任務
* @return 子任務執行狀態。1表示執行成功,2表示竊取到的任務爲null,可以重試,-1表示非空,但跟task不匹配
* 若隊列爲空,則爲負數,且剩下31位的值爲該隊列的base
 */
final int pollAndExecCC(CountedCompleter<?> task) {
    int b, h; ForkJoinTask<?>[] a; Object o;
    if ((b = base) - top >= 0 || (a = array) == null)
        h = b | Integer.MIN_VALUE;  // to sense movement on re-poll
    else {
        long j = (((a.length - 1) & b) << ASHIFT) + ABASE;
        if ((o = U.getObjectVolatile(a, j)) == null)
            h = 2;                  // retryable
        else if (!(o instanceof CountedCompleter))
            h = -1;                 // unmatchable
        else {
            CountedCompleter<?> t = (CountedCompleter<?>)o;
            for (CountedCompleter<?> r = t;;) {
                if (r == task) {
                    if (base == b &&
                        U.compareAndSwapObject(a, j, t, null)) {
                        base = b + 1;
                        t.doExec();
                        h = 1;      // success
                    }
                    else
                        h = 2;      // lost CAS
                    break;
                }
                else if ((r = r.completer) == null) {
                    h = -1;         // unmatched
                    break;
                }
            }
        }
    }
    return h;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章