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