使用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 实现同步到主线程