使用ForkJoinPool
的時候發現執行任務的方法有:
- invoke(ForkJoinTask task)
- execute(ForkJoinTask<?> task)
- submit(ForkJoinTask task)
三種方式,現在總結下區別。
首先比較execute和submit的區別,觀察源碼發現:
/**
* Arranges for (asynchronous) execution of the given task.
*
* @param task the task
* @throws NullPointerException if the task is null
* @throws RejectedExecutionException if the task cannot be
* scheduled for execution
*/
public void execute(ForkJoinTask<?> task) {
if (task == null)
throw new NullPointerException();
externalPush(task);
}
/**
* Submits a ForkJoinTask for execution.
*
* @param task the task to submit
* @param <T> the type of the task's result
* @return the task
* @throws NullPointerException if the task is null
* @throws RejectedExecutionException if the task cannot be
* scheduled for execution
*/
public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) {
if (task == null)
throw new NullPointerException();
externalPush(task);
return task;
}
從方法體和返回參數可以看出,兩者邏輯大致相同,都是首先對任務做非空校驗,再將任務壓入執行隊列,唯一的不同是submit會把任務對象本身返回,返回後我們可以通過get()
獲取方法執行結果。
再看invoke()
/**
* Performs the given task, returning its result upon completion.
* If the computation encounters an unchecked Exception or Error,
* it is rethrown as the outcome of this invocation. Rethrown
* exceptions behave in the same way as regular exceptions, but,
* when possible, contain stack traces (as displayed for example
* using {@code ex.printStackTrace()}) of both the current thread
* as well as the thread actually encountering the exception;
* minimally only the latter.
*
* @param task the task
* @param <T> the type of the task's result
* @return the task's result
* @throws NullPointerException if the task is null
* @throws RejectedExecutionException if the task cannot be
* scheduled for execution
*/
public <T> T invoke(ForkJoinTask<T> task) {
if (task == null)
throw new NullPointerException();
externalPush(task);
return task.join();
}
和submit()
不同的是返回task.join()
,再看join()
方法
/**
* Returns the result of the computation when it {@link #isDone is
* done}. This method differs from {@link #get()} in that
* abnormal completion results in {@code RuntimeException} or
* {@code Error}, not {@code ExecutionException}, and that
* interrupts of the calling thread do <em>not</em> cause the
* method to abruptly return by throwing {@code
* InterruptedException}.
*
* @return the computed result
*/
public final V join() {
int s;
if ((s = doJoin() & DONE_MASK) != NORMAL)
reportException(s);
return getRawResult();
}
首先判斷任務是否執行完畢,若未執行完畢,拋出任務取消或者任務異常的異常Exception,否則獲取任務執行結果。而getRawResult()
就是取任務的結果返回。這裏看判斷任務執行完畢的doJoin()
方法
/**
* Implementation for join, get, quietlyJoin. Directly handles
* only cases of already-completed, external wait, and
* unfork+exec. Others are relayed to ForkJoinPool.awaitJoin.
*
* @return status upon completion
*/
private int doJoin() {
int s; Thread t; ForkJoinWorkerThread wt; ForkJoinPool.WorkQueue w;
return (s = status) < 0 ? s :
((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
(w = (wt = (ForkJoinWorkerThread)t).workQueue).
tryUnpush(this) && (s = doExec()) < 0 ? s :
wt.pool.awaitJoin(w, this, 0L) :
externalAwaitDone();
}
這裏用了多個三元運算符,整理下邏輯
- 判斷當前任務的執行狀態
- 當前狀態不是已完成
- 當前線程是ForkJoin線程
- 從工作隊列中取出一個任務執行
- 否則等待當前任務執行完畢
- 當前線程是ForkJoin線程
- 當前狀態不是已完成
所以invoke()
方法的調用,會一直阻塞到任務執行完成返回
總結一下:
- execute(ForkJoinTask) 異步執行tasks,無返回值
- invoke(ForkJoinTask) 有Join, tasks會被同步到主進程
- submit(ForkJoinTask) 異步執行,且帶Task返回值,可通過task.get 實現同步到主線程