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;
}

 

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