概述
简单的说,ForkJoinTask将任务fork成足够小的任务,并发解决这些小任务,然后将这些小任务结果join。这种思想充分利用了CPU的多核系统,使得CPU的利用率得到大幅度提升,减少了任务执行时间。
该类提供了将任务分割成子任务的方法fork、等待子任务完成的方法join,通常情况下,我们将一个大的任务fork成两个子任务,再通过join等待子任务完成。ForkJoinTask是一个抽象类,它有两个子类:RecursiveTask、RecursiveAction、CountedCompleter,这三个类也是抽象类。
RecursiveTask:有返回值的任务RecursiveAction:没有返回值的任务CountedCompleter:在任务完成执行后会触发执行一个自定义的钩子函数
在实际运用中,我们一般都会继承 RecursiveTask 、RecursiveAction 或 CountedCompleter 来实现我们的业务需求,而不会直接继承 ForkJoinTask 类。
ForkJoinTask 实现了 Future 接口,说明它也是一个可取消的异步运算任务,实际上ForkJoinTask 是 Future 的轻量级实现,主要用在纯粹是计算的函数式任务或者操作完全独立的对象计算任务。fork 是主运行方法,用于异步执行;而 join 方法在任务结果计算完毕之后才会运行,用来合并或返回计算结果。
JDK 对于ForkJoinTask的解释
/**
* Abstract base class for tasks that run within a {@link ForkJoinPool}.
* A {@code ForkJoinTask} is a thread-like entity that is much
* lighter weight than a normal thread. Huge numbers of tasks and
* subtasks may be hosted by a small number of actual threads in a
* ForkJoinPool, at the price of some usage limitations.
*
* <p>A "main" {@code ForkJoinTask} begins execution when it is
* explicitly submitted to a {@link ForkJoinPool}, or, if not already
* engaged in a ForkJoin computation, commenced in the {@link
* ForkJoinPool#commonPool()} via {@link #fork}, {@link #invoke}, or
* related methods. Once started, it will usually in turn start other
* subtasks. As indicated by the name of this class, many programs
* using {@code ForkJoinTask} employ only methods {@link #fork} and
* {@link #join}, or derivatives such as {@link
* #invokeAll(ForkJoinTask...) invokeAll}. However, this class also
* provides a number of other methods that can come into play in
* advanced usages, as well as extension mechanics that allow support
* of new forms of fork/join processing.
*
* <p>A {@code ForkJoinTask} is a lightweight form of {@link Future}.
* The efficiency of {@code ForkJoinTask}s stems from a set of
* restrictions (that are only partially statically enforceable)
* reflecting their main use as computational tasks calculating pure
* functions or operating on purely isolated objects. The primary
* coordination mechanisms are {@link #fork}, that arranges
* asynchronous execution, and {@link #join}, that doesn't proceed
* until the task's result has been computed. Computations should
* ideally avoid {@code synchronized} methods or blocks, and should
* minimize other blocking synchronization apart from joining other
* tasks or using synchronizers such as Phasers that are advertised to
* cooperate with fork/join scheduling. Subdividable tasks should also
* not perform blocking I/O, and should ideally access variables that
* are completely independent of those accessed by other running
* tasks. These guidelines are loosely enforced by not permitting
* checked exceptions such as {@code IOExceptions} to be
* thrown. However, computations may still encounter unchecked
* exceptions, that are rethrown to callers attempting to join
* them. These exceptions may additionally include {@link
* RejectedExecutionException} stemming from internal resource
* exhaustion, such as failure to allocate internal task
* queues. 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 thread
* that initiated the computation as well as the thread actually
* encountering the exception; minimally only the latter.
*
* <p>It is possible to define and use ForkJoinTasks that may block,
* but doing do requires three further considerations: (1) Completion
* of few if any <em>other</em> tasks should be dependent on a task
* that blocks on external synchronization or I/O. Event-style async
* tasks that are never joined (for example, those subclassing {@link
* CountedCompleter}) often fall into this category. (2) To minimize
* resource impact, tasks should be small; ideally performing only the
* (possibly) blocking action. (3) Unless the {@link
* ForkJoinPool.ManagedBlocker} API is used, or the number of possibly
* blocked tasks is known to be less than the pool's {@link
* ForkJoinPool#getParallelism} level, the pool cannot guarantee that
* enough threads will be available to ensure progress or good
* performance.
*
* <p>The primary method for awaiting completion and extracting
* results of a task is {@link #join}, but there are several variants:
* The {@link Future#get} methods support interruptible and/or timed
* waits for completion and report results using {@code Future}
* conventions. Method {@link #invoke} is semantically
* equivalent to {@code fork(); join()} but always attempts to begin
* execution in the current thread. The "<em>quiet</em>" forms of
* these methods do not extract results or report exceptions. These
* may be useful when a set of tasks are being executed, and you need
* to delay processing of results or exceptions until all complete.
* Method {@code invokeAll} (available in multiple versions)
* performs the most common form of parallel invocation: forking a set
* of tasks and joining them all.
*
* <p>In the most typical usages, a fork-join pair act like a call
* (fork) and return (join) from a parallel recursive function. As is
* the case with other forms of recursive calls, returns (joins)
* should be performed innermost-first. For example, {@code a.fork();
* b.fork(); b.join(); a.join();} is likely to be substantially more
* efficient than joining {@code a} before {@code b}.
*
* <p>The execution status of tasks may be queried at several levels
* of detail: {@link #isDone} is true if a task completed in any way
* (including the case where a task was cancelled without executing);
* {@link #isCompletedNormally} is true if a task completed without
* cancellation or encountering an exception; {@link #isCancelled} is
* true if the task was cancelled (in which case {@link #getException}
* returns a {@link java.util.concurrent.CancellationException}); and
* {@link #isCompletedAbnormally} is true if a task was either
* cancelled or encountered an exception, in which case {@link
* #getException} will return either the encountered exception or
* {@link java.util.concurrent.CancellationException}.
*
* <p>The ForkJoinTask class is not usually directly subclassed.
* Instead, you subclass one of the abstract classes that support a
* particular style of fork/join processing, typically {@link
* RecursiveAction} for most computations that do not return results,
* {@link RecursiveTask} for those that do, and {@link
* CountedCompleter} for those in which completed actions trigger
* other actions. Normally, a concrete ForkJoinTask subclass declares
* fields comprising its parameters, established in a constructor, and
* then defines a {@code compute} method that somehow uses the control
* methods supplied by this base class.
*
* <p>Method {@link #join} and its variants are appropriate for use
* only when completion dependencies are acyclic; that is, the
* parallel computation can be described as a directed acyclic graph
* (DAG). Otherwise, executions may encounter a form of deadlock as
* tasks cyclically wait for each other. However, this framework
* supports other methods and techniques (for example the use of
* {@link Phaser}, {@link #helpQuiesce}, and {@link #complete}) that
* may be of use in constructing custom subclasses for problems that
* are not statically structured as DAGs. To support such usages, a
* ForkJoinTask may be atomically <em>tagged</em> with a {@code short}
* value using {@link #setForkJoinTaskTag} or {@link
* #compareAndSetForkJoinTaskTag} and checked using {@link
* #getForkJoinTaskTag}. The ForkJoinTask implementation does not use
* these {@code protected} methods or tags for any purpose, but they
* may be of use in the construction of specialized subclasses. For
* example, parallel graph traversals can use the supplied methods to
* avoid revisiting nodes/tasks that have already been processed.
* (Method names for tagging are bulky in part to encourage definition
* of methods that reflect their usage patterns.)
*
* <p>Most base support methods are {@code final}, to prevent
* overriding of implementations that are intrinsically tied to the
* underlying lightweight task scheduling framework. Developers
* creating new basic styles of fork/join processing should minimally
* implement {@code protected} methods {@link #exec}, {@link
* #setRawResult}, and {@link #getRawResult}, while also introducing
* an abstract computational method that can be implemented in its
* subclasses, possibly relying on other {@code protected} methods
* provided by this class.
*
* <p>ForkJoinTasks should perform relatively small amounts of
* computation. Large tasks should be split into smaller subtasks,
* usually via recursive decomposition. As a very rough rule of thumb,
* a task should perform more than 100 and less than 10000 basic
* computational steps, and should avoid indefinite looping. If tasks
* are too big, then parallelism cannot improve throughput. If too
* small, then memory and internal task maintenance overhead may
* overwhelm processing.
*
* <p>This class provides {@code adapt} methods for {@link Runnable}
* and {@link Callable}, that may be of use when mixing execution of
* {@code ForkJoinTasks} with other kinds of tasks. When all tasks are
* of this form, consider using a pool constructed in <em>asyncMode</em>.
*
* <p>ForkJoinTasks are {@code Serializable}, which enables them to be
* used in extensions such as remote execution frameworks. It is
* sensible to serialize tasks only before or after, but not during,
* execution. Serialization is not relied on during execution itself.
*
* @since 1.7
* @author Doug Lea
*/
继承类型
public abstract class ForkJoinTask<V> implements Future<V>, Serializable
源码解析
JDK解释
/*
* See the internal documentation of class ForkJoinPool for a
* general implementation overview. ForkJoinTasks are mainly
* responsible for maintaining their "status" field amidst relays
* to methods in ForkJoinWorkerThread and ForkJoinPool.
*
* The methods of this class are more-or-less layered into
* (1) basic status maintenance
* (2) execution and awaiting completion
* (3) user-level methods that additionally report results.
* This is sometimes hard to see because this file orders exported
* methods in a way that flows well in javadocs.
*/
/*
* The status field holds run control status bits packed into a
* single int to minimize footprint and to ensure atomicity (via
* CAS). Status is initially zero, and takes on nonnegative
* values until completed, upon which status (anded with
* DONE_MASK) holds value NORMAL, CANCELLED, or EXCEPTIONAL. Tasks
* undergoing blocking waits by other threads have the SIGNAL bit
* set. Completion of a stolen task with SIGNAL set awakens any
* waiters via notifyAll. Even though suboptimal for some
* purposes, we use basic builtin wait/notify to take advantage of
* "monitor inflation" in JVMs that we would otherwise need to
* emulate to avoid adding further per-task bookkeeping overhead.
* We want these monitors to be "fat", i.e., not use biasing or
* thin-lock techniques, so use some odd coding idioms that tend
* to avoid them, mainly by arranging that every synchronized
* block performs a wait, notifyAll or both.
*
* These control bits occupy only (some of) the upper half (16
* bits) of status field. The lower bits are used for user-defined
* tags.
*/
常量
/** The run status of this task */
volatile int status; // accessed directly by pool and workers
static final int DONE_MASK = 0xf0000000; // mask out non-completion bits
static final int NORMAL = 0xf0000000; // must be negative
static final int CANCELLED = 0xc0000000; // must be < NORMAL
static final int EXCEPTIONAL = 0x80000000; // must be < CANCELLED
static final int SIGNAL = 0x00010000; // must be >= 1 << 16
static final int SMASK = 0x0000ffff; // short bits for tags
<1> DONE_MASK/NORMAL
0xf0000000 = 15 * 16 ^ 7 = 15 * 2^ 28, 也就是 1111 0000 0000 0000 0000 0000 0000 0000
<2> CANCELLED
0xc0000000 = 12 * 16 ^ 7 = 12 * 2^ 28, 就是 1100 0000 0000 0000 0000 0000 0000 0000
<3> EXCEPTIONAL
0x80000000 = 8 * 16 ^ 7 = 8 * 2^ 28, 就是 1000 0000 0000 0000 0000 0000 0000 0000
<4> SIGNAL
0x00010000 = 1 * 16 ^ 4 = 1 * 2^ 16, 就是 0000 0000 0000 0001 0000 0000 0000 0000
<5> SMASK
0x0000ffff = 15 * 16 ^ 3 + 15 * 16 ^ 2 + 15 * 16 ^ 1 + 15 * 16 ^ 0= 15 * (1 + 2 ^ 4 + 2 ^ 8 + 2 ^ 12) = (8 + 4 + 2 + 1) * (2 ^ 12 + 2 ^ 8 + 2 ^ 4 + 2 ^ 0)
就是 0000 0000 0000 0000 1111 1111 1111 1111
具体的区别在于
任务的状态有四种:
状态
值
说明
NORMAL
0xf0000000
表示任务“正常”完成的状态
CANCELLED
0xc0000000
表示任务“取消”完成的状态
EXCEPTIONAL
0x80000000
表示任务“异常”完成的状态
SIGNAL
0x00010000
信号,有其他任务依赖当前任务,任务结束前,通知其他任务join当前任务的结果。
未完成状态包括初始状态0和SIGNAL
还有两个掩码
标识
值
说明
DONE_MASK
0xf0000000
任务完成状态标志位
SMASK
0x0000ffff
低位掩码,也是最大索引位(非任务状态
基本方法
setCompletion()
/**
* Marks completion and wakes up threads waiting to join this
* task.
*
* @param completion one of NORMAL, CANCELLED, EXCEPTIONAL
* @return completion status on exit
*/
private int setCompletion(int completion) {
/*设置任务完成状态,completion可选项为:NORMAL、CANCELLED、EXCEPTIONAL*/
for (int s;;) {
/*状态值为负数,说明任务已经完成*/
if ((s = status) < 0)
return s;
if (U.compareAndSwapInt(this, STATUS, s, s | completion)) {
/*如果原状态为SIGNAL,通知其他在该任务上等待的线程join该任务的结果*/
if ((s >>> 16) != 0)
synchronized (this) { notifyAll(); }
return completion;
}
}
}
doExec()
/**
* Primary execution method for stolen tasks. Unless done, calls
* exec and records status if completed, but doesn't wait for
* completion otherwise.
*
* @return status on exit from this method
*/
final int doExec() {
int s; boolean completed;
/*如果任务已经完成了,直接返回任务状态*/
if ((s = status) >= 0) {
try {
/*exec返回任务是否正常完成,exec是一个抽象方法,为了扩展,留给子类实现*/
completed = exec();
} catch (Throwable rex) {
/*如果任务执行过程中出现异常,则设置为异常完成*/
return setExceptionalCompletion(rex);
}
if (completed)
/*如果任务正常执行完成,把任务状态设置为正常完成*/
s = setCompletion(NORMAL);
}
return s;
}
doExec() 包含了任务执行逻辑
fork()
/*
* Arranges to asynchronously execute this task in the pool the
* current task is running in, if applicable, or using the {@link
* ForkJoinPool#commonPool()} if not {@link #inForkJoinPool}. While
* it is not necessarily enforced, it is a usage error to fork a
* task more than once unless it has completed and been
* reinitialized. Subsequent modifications to the state of this
* task or any data it operates on are not necessarily
* consistently observable by any thread other than the one
* executing it unless preceded by a call to {@link #join} or
* related methods, or a call to {@link #isDone} returning {@code
* true}.
*/
public final ForkJoinTask<V> fork() {
Thread t;
if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
/*如果当前线程是ForkJoinWorkerThread,直接把任务push到自己所拥有的队列的top位置。*/
((ForkJoinWorkerThread)t).workQueue.push(this);
else
/*如果是非工作线程,就是一个提交到Pool的过程。*/
ForkJoinPool.common.externalPush(this);
return this;
}
fork方法是属于所有方法的基石,将我们的任务进行拆分
这里需要注意的是workqueue是ForkJoinPool的一个内部类对象.并且workQueue不是没有继承queue接口
complete()
/**
* Completes this task, and if not already aborted or cancelled,
* returning the given value as the result of subsequent
* invocations of {@code join} and related operations. This method
* may be used to provide results for asynchronous tasks, or to
* provide alternative handling for tasks that would not otherwise
* complete normally. Its use in other situations is
* discouraged. This method is overridable, but overridden
* versions must invoke {@code super} implementation to maintain
* guarantees.
*/
public void complete(V value) {
try {
/*设置结果*/
setRawResult(value);
} catch (Throwable rex) {
setExceptionalCompletion(rex);
return;
}
/*设置正常完成*/
setCompletion(NORMAL);
}
setExceptionalCompletion()
/**
* Records exception and possibly propagates.
*
* @return status on exit
*/
private int setExceptionalCompletion(Throwable ex) {
int s = recordExceptionalCompletion(ex);
if ((s & DONE_MASK) == EXCEPTIONAL)
/*内部传播异常*/
internalPropagateException(ex);
return s;
}
recordExceptionalCompletion()
final int recordExceptionalCompletion(Throwable ex) {
int s;
/*获取任务状态,如果任务状态已经完成了,直接返回任务状态*/
if ((s = status) >= 0) {
/*
* System.identityHashCode和Object.hashCode返回的值一样,
* 都是根据对象在内存中的地址计算出来的哈希码
* */
int h = System.identityHashCode(this);
/*操作异常任务表之前先获取锁*/
final ReentrantLock lock = exceptionTableLock;
lock.lock();
try {
/*
* 删除已经被回收对象对应的弱引用,该方法会遍历exceptionTableRefQueue,
* 并删除exceptionTable中对应的弱引用
* */
expungeStaleExceptions();
/*
* 将执行过程抛出异常的任务弱引用保存到exceptionTable,这里其实是将
* exceptionTable当作哈希表使用,i就是保存的位置
* */
ExceptionNode[] t = exceptionTable;
int i = h & (t.length - 1);
/*
* 遍历哈希表索引i处的链表,如果遍历过程中发现已经存在该任务,
* 跳出循环,否则遍历到链表末尾时,创建新的ExceptionNode,
* 并将该节点放到链表的头部
* */
for (ExceptionNode e = t[i]; ; e = e.next) {
if (e == null) {
t[i] = new ExceptionNode(this, ex, t[i]);
break;
}
if (e.get() == this) // already present
break;
}
} finally {
lock.unlock();
}
/*设置任务的完成状态为EXCEPTIONAL*/
s = setCompletion(EXCEPTIONAL);
}
return s;
}
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)
/*如果执行doJoin方法后不是正常完成,就报告异常信息*/
reportException(s);
/*如果执行doJoin方法后正常完成就返结果*/
return getRawResult();
}
/**
* 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;
/*
* 如果当前任务状态已经完成,直接返回任务状态;
* 否则的话,判断当前线程是不是ForkJoinWorkerThread线程,如果不是执行externalAwaitDone方法
* 如果是的话,判断如果需要join的任务刚刚好是当前线程所拥有的队列的top位置,
* 这意味着当前工作线程下一个就将执行到它,则执行它。
* 否则的话调用awaitJoin方法
* */
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();
}
join()合并结果
externalAwaitDone的执行首先会设置任务的status为signal状态,这样该任务执行结束之后会调用notifyAll来唤醒自己;其次,阻塞自己,直到任务执行完成后把自己唤醒。
invoke()/doInvoke()
/**
* Commences performing this task, awaits its completion if
* necessary, and returns its result, or throws an (unchecked)
* {@code RuntimeException} or {@code Error} if the underlying
* computation did so.
*
* @return the computed result
*/
public final V invoke() {
int s;
/*执行doInvoke,如果结果不是正常完成,则执行reportException*/
if ((s = doInvoke() & DONE_MASK) != NORMAL)
reportException(s);
/*否则的话执行getRawResult获取结果*/
return getRawResult();
}
//Implementation for invoke, quietlyInvoke.
private int doInvoke() {
int s; Thread t; ForkJoinWorkerThread wt;
/*
* 如果当前任务执行后状态完成了,则返回执行后的状态
* 否则的话,判断当前线程是否为ForkJoinWorkerThread线程
* 如果是的话执行awaitJoin
* 否则的话执行externalAwaitDone
* */
return (s = doExec()) < 0 ? s :
((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
(wt = (ForkJoinWorkerThread)t).pool.
awaitJoin(wt.workQueue, this, 0L) :
externalAwaitDone();
}
ForkJoinTask的join()和invoke()方法都可以用来获取任务的执行结果(另外还有get方法也是调用了doJoin来获取任务结果,但是会响应运行时异常),它们对外部提交任务的执行方式一致,都是通过externalAwaitDone方法等待执行结果。不同的是invoke()方法会直接执行当前任务;而join()方法则是在当前任务在队列 top 位时(通过tryUnpush方法判断)才能执行,如果当前任务不在 top 位或者任务执行失败调用ForkJoinPool.awaitJoin方法帮助执行或阻塞当前 join 任务。(所以在官方文档中建议了我们对ForkJoinTask任务的调用顺序,一对 fork-join操作一般按照如下顺序调用:a.fork(); b.fork(); b.join(); a.join();。因为任务 b 是后面进入队列,也就是说它是在栈顶的(top 位),在它fork()之后直接调用join()就可以直接执行而不会调用ForkJoinPool.awaitJoin方法去等待。)
externalInterruptibleAwaitDone()
/**
* Blocks a non-worker-thread until completion or interruption.
*/
private int externalInterruptibleAwaitDone() throws InterruptedException {
int s;
if (Thread.interrupted())
throw new InterruptedException();
if ((s = status) >= 0 &&
(s = ((this instanceof CountedCompleter) ?
ForkJoinPool.common.externalHelpComplete(
(CountedCompleter<?>)this, 0) :
ForkJoinPool.common.tryExternalUnpush(this) ? doExec() :
0)) >= 0) {
while ((s = status) >= 0) {
if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
synchronized (this) {
if (status >= 0)
wait(0L);
else
notifyAll();
}
}
}
}
return s;
}
externalAwaitDone()
/**
* Blocks a non-worker-thread until completion.
* @return status upon completion
*/
private int externalAwaitDone() {
int s = ((this instanceof CountedCompleter) ? // try helping
ForkJoinPool.common.externalHelpComplete(
(CountedCompleter<?>)this, 0) :
ForkJoinPool.common.tryExternalUnpush(this) ? doExec() : 0);
if (s >= 0 && (s = status) >= 0) {
boolean interrupted = false;
do {
if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
synchronized (this) {
if (status >= 0) {
try {
wait(0L);
} catch (InterruptedException ie) {
interrupted = true;
}
}
else
notifyAll();
}
}
} while ((s = status) >= 0);
if (interrupted)
Thread.currentThread().interrupt();
}
return s;
}
tryUnfork()
/**
* Tries to unschedule this task for execution. This method will
* typically (but is not guaranteed to) succeed if this task is
* the most recently forked task by the current thread, and has
* not commenced executing in another thread. This method may be
* useful when arranging alternative local processing of tasks
* that could have been, but were not, stolen.
*
* @return {@code true} if unforked
*/
public boolean tryUnfork() {
Thread t;
return (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
((ForkJoinWorkerThread)t).workQueue.tryUnpush(this) :
ForkJoinPool.common.tryExternalUnpush(this));
}
tryUnfork方法尝试将该任务从任务队列中弹出。该任务不再被线程池调度。
get()/get(long timeout, TimeUnit unit)
/**
* Waits if necessary for the computation to complete, and then
* retrieves its result.
*
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an
* exception
* @throws InterruptedException if the current thread is not a
* member of a ForkJoinPool and was interrupted while waiting
*/
public final V get() throws InterruptedException, ExecutionException {
int s = (Thread.currentThread() instanceof ForkJoinWorkerThread) ?
doJoin() : externalInterruptibleAwaitDone();
Throwable ex;
if ((s &= DONE_MASK) == CANCELLED)
throw new CancellationException();
if (s == EXCEPTIONAL && (ex = getThrowableException()) != null)
throw new ExecutionException(ex);
return getRawResult();
}
public final V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
int s;
long nanos = unit.toNanos(timeout);
if (Thread.interrupted())
throw new InterruptedException();
if ((s = status) >= 0 && nanos > 0L) {
long d = System.nanoTime() + nanos;
long deadline = (d == 0L) ? 1L : d; // avoid 0
Thread t = Thread.currentThread();
if (t instanceof ForkJoinWorkerThread) {
ForkJoinWorkerThread wt = (ForkJoinWorkerThread)t;
s = wt.pool.awaitJoin(wt.workQueue, this, deadline);
}
else if ((s = ((this instanceof CountedCompleter) ?
ForkJoinPool.common.externalHelpComplete(
(CountedCompleter<?>)this, 0) :
ForkJoinPool.common.tryExternalUnpush(this) ?
doExec() : 0)) >= 0) {
long ns, ms; // measure in nanosecs, but wait in millisecs
while ((s = status) >= 0 &&
(ns = deadline - System.nanoTime()) > 0L) {
if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) > 0L &&
U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
synchronized (this) {
if (status >= 0)
wait(ms); // OK to throw InterruptedException
else
notifyAll();
}
}
}
}
}
if (s >= 0)
s = status;
if ((s &= DONE_MASK) != NORMAL) {
Throwable ex;
if (s == CANCELLED)
throw new CancellationException();
if (s != EXCEPTIONAL)
throw new TimeoutException();
if ((ex = getThrowableException()) != null)
throw new ExecutionException(ex);
}
return getRawResult();
}
如果当前线程是ForkJoinWorkerThread,调用doJoin方法获取结果,该方法前面已经讲过了。如果当前线程不是ForkerJoinWorkerThread,调用externalInterruptibleAwaitDone方法。 任务执行完成返回后,如果任务完成状态是CANCELLED,抛出CancellationException异常。如果任务完成状态是EXCEPTIONAL,将任务执行过程中抛出的异常包装成ExecutionExcepiton重新抛出。
抽象方法
getRawResult()/setRawResult()
/**
* Returns the result that would be returned by {@link #join}, even
* if this task completed abnormally, or {@code null} if this task
* is not known to have been completed. This method is designed
* to aid debugging, as well as to support extensions. Its use in
* any other context is discouraged.
*
* @return the result, or {@code null} if not completed
*/
public abstract V getRawResult();
/**
* Forces the given value to be returned as a result. This method
* is designed to support extensions, and should not in general be
* called otherwise.
*/
protected abstract void setRawResult(V value);
exec()
/**
* Immediately performs the base action of this task and returns
* true if, upon return from this method, this task is guaranteed
* to have completed normally. This method may return false
* otherwise, to indicate that this task is not necessarily
* complete (or is not known to be complete), for example in
* asynchronous actions that require explicit invocations of
* completion methods. This method may also throw an (unchecked)
* exception to indicate abnormal exit. This method is designed to
* support extensions, and should not in general be called
* otherwise.
*
* @return {@code true} if this task is known to have completed normally
*/
protected abstract boolean exec();
exec是一个抽象方法,完成具体任务的代码,由子类实现,该方法返回任务是否正常完成。任务执行过程如果抛出异常,捕获异常并设置异常完成状态。如果任务正常完成,设置正常状态并通知其他需要join该任务的线程,其他需要join该任务的线程通常是一个等待父任务完成的线程,也就是说,此时当前任务其实是个子任务,子任务结束后,父任务就可以尝试合并子任务的执行结果了,看下示例图:
任务执行过程抛出异常时,调用者可以获取该异常,ForkJoinTask并没有直接将异常的任务保存起来,而是保存了异常任务的弱引用,在合适的时候,GC将会回收该异常任务,被回收对象对应的弱引用将会保存在弱引用队列中。
异常处理
任务执行过程抛出异常时,调用者可以获取该异常,ForkJoinTask并没有直接将异常的任务保存起来,而是保存了异常任务的弱引用,在合适的时候,GC将会回收该异常任务,被回收对象对应的弱引用将会保存在弱引用队列中。
private static final ExceptionNode[] exceptionTable;
private static final ReentrantLock exceptionTableLock;
private static final ReferenceQueue<Object> exceptionTableRefQueue;
static final class ExceptionNode extends WeakReference<ForkJoinTask<?>> {
final Throwable ex;
ExceptionNode next;
final long thrower; // use id not ref to avoid weak cycles
final int hashCode; // store task hashCode before weak ref disappears
ExceptionNode(ForkJoinTask<?> task, Throwable ex, ExceptionNode next) {
super(task, exceptionTableRefQueue);
this.ex = ex;
this.next = next;
this.thrower = Thread.currentThread().getId();
this.hashCode = System.identityHashCode(task);
}
}
ExceptionNode继承了WeakReference类,是一个弱引用,保存了执行过程中抛出异常的ForkJoinTask。exceptionTable是一个ExceptionNode数组exceptionTableLock锁exceptionTableRefQueue保存弱引用的queue,GC时将回收的对象对应的弱引用保存到该队列中
静态类
AdaptedRunnable
/**
* Adaptor for Runnables. This implements RunnableFuture
* to be compliant with AbstractExecutorService constraints
* when used in ForkJoinPool.
*/
static final class AdaptedRunnable<T> extends ForkJoinTask<T>
implements RunnableFuture<T> {
final Runnable runnable;
T result;
AdaptedRunnable(Runnable runnable, T result) {
if (runnable == null) throw new NullPointerException();
this.runnable = runnable;
this.result = result; // OK to set this even before completion
}
public final T getRawResult() { return result; }
public final void setRawResult(T v) { result = v; }
public final boolean exec() { runnable.run(); return true; }
public final void run() { invoke(); }
private static final long serialVersionUID = 5232453952276885070L;
}
AdaptedRunnableAction
/**
* Adaptor for Runnables without results
*/
static final class AdaptedRunnableAction extends ForkJoinTask<Void>
implements RunnableFuture<Void> {
final Runnable runnable;
AdaptedRunnableAction(Runnable runnable) {
if (runnable == null) throw new NullPointerException();
this.runnable = runnable;
}
public final Void getRawResult() { return null; }
public final void setRawResult(Void v) { }
public final boolean exec() { runnable.run(); return true; }
public final void run() { invoke(); }
private static final long serialVersionUID = 5232453952276885070L;
}
RunnableExecuteAction
/**
* Adaptor for Runnables in which failure forces worker exception
*/
static final class RunnableExecuteAction extends ForkJoinTask<Void> {
final Runnable runnable;
RunnableExecuteAction(Runnable runnable) {
if (runnable == null) throw new NullPointerException();
this.runnable = runnable;
}
public final Void getRawResult() { return null; }
public final void setRawResult(Void v) { }
public final boolean exec() { runnable.run(); return true; }
void internalPropagateException(Throwable ex) {
rethrow(ex); // rethrow outside exec() catches.
}
private static final long serialVersionUID = 5232453952276885070L;
}
AdaptedCallable
/**
* Adaptor for Callables
*/
static final class AdaptedCallable<T> extends ForkJoinTask<T>
implements RunnableFuture<T> {
final Callable<? extends T> callable;
T result;
AdaptedCallable(Callable<? extends T> callable) {
if (callable == null) throw new NullPointerException();
this.callable = callable;
}
public final T getRawResult() { return result; }
public final void setRawResult(T v) { result = v; }
public final boolean exec() {
try {
result = callable.call();
return true;
} catch (Error err) {
throw err;
} catch (RuntimeException rex) {
throw rex;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public final void run() { invoke(); }
private static final long serialVersionUID = 2838392045355241008L;
}
静态类衍生的方法
adapt(Runnable runnable, T result)
/**
* Returns a new {@code ForkJoinTask} that performs the {@code run}
* method of the given {@code Runnable} as its action, and returns
* the given result upon {@link #join}.
*
* @param runnable the runnable action
* @param result the result upon completion
* @param <T> the type of the result
* @return the task
*/
public static <T> ForkJoinTask<T> adapt(Runnable runnable, T result) {
return new AdaptedRunnable<T>(runnable, result);
}
adapt(Runnable runnable)
/**
* Returns a new {@code ForkJoinTask} that performs the {@code run}
* method of the given {@code Runnable} as its action, and returns
* a null result upon {@link #join}.
*
* @param runnable the runnable action
* @return the task
*/
public static ForkJoinTask<?> adapt(Runnable runnable) {
return new AdaptedRunnableAction(runnable);
}
adapt(Callable<? extends T> callable)
/**
* Returns a new {@code ForkJoinTask} that performs the {@code call}
* method of the given {@code Callable} as its action, and returns
* its result upon {@link #join}, translating any checked exceptions
* encountered into {@code RuntimeException}.
*
* @param callable the callable action
* @param <T> the type of the callable's result
* @return the task
*/
public static <T> ForkJoinTask<T> adapt(Callable<? extends T> callable) {
return new AdaptedCallable<T>(callable);
}
总结
1,可以使用invokeAll(task)方法,主动执行其它的ForkJoinTask,并等待Task完成。(是同步的)
2,还可以使用fork方法,让一个task执行(这个方法是异步的)
3,还可以使用join方法,让一个task执行(这个方法是同步的,它和fork不同点是同步或者异步的区别)
4,可以使用join来取得ForkJoinTask的返回值。由于RecursiveTask类实现了Future接口,所以也可以使用get()取得返回值。
get()和join()有两个主要的区别:
join()方法不能被中断。如果你中断调用join()方法的线程,这个方法将抛InterruptedException异常。
如果任务抛出任何未受检异常,get()方法将返回一个ExecutionException异常,而join()方法将返回一个RuntimeException异常。
5,ForkJoinTask在不显示使用ForkJoinPool.execute/invoke/submit()方法进行执行的情况下,也可以使用自己的fork/invoke方法进行执行。
使用fork/invoke方法执行时,其实原理也是在ForkJoinPool里执行,只不过使用的是一个“在ForkJoinPool内部生成的静态的ForkJoinPool。
6,ForkJoinTask有两个子类,RecursiveAction和RecursiveTask。他们之间的区别是,RecursiveAction没有返回值,RecursiveTask有返回值。
7,看看ForkjoinTask的Complete方法的使用场景
这个方法好要是用来使一个任务结束。这个方法被用在结束异步任务上,或者为那些能不正常结束的任务,提供一个选择。
8,Task的completeExceptionally方法是怎么回事。
这个方法被用来,在异步的Task中产生一个exception,或者强制结束那些“不会结束”的任务
这个方法是在Task想要“自己结束自己”时,可以被使用。而cancel方法,被设计成被其它TASK调用。
当你在一个任务中抛出一个未检查异常时,它也影响到它的父任务(把它提交到ForkJoinPool类的任务)和父任务的父任务,以此类推。