目录
2)、FutureTask实现原理(LockSupport的park、parkNanos + 自旋、unpark实现的等待唤醒机制)
我们知道并发编程可以使用Runnable【源码标注@since jdk1.0】不带返回值的回调函数,也可以使用Callable<V> 【源码标注Callable @since 1.5 并且由并发大师Doug Lea编写】带返回值的回调函数。也就是从jdk5之后才有带返回值的编程方式,那么需要对原有的进行扩展、兼容,再具体进行实现。
扩展:Future接口
兼容:RunnableFuture接口
实现类:FutureTask类(或者其扩展子类;或者RunnableFuture的其他实现类)
1、Future
针对带返回值方式的异步线程任务,设置了一个标准(接口Future),其最最要的接口如上如:SheduledFuture、CompletableFuture、ForkJoinFuture这些之前都大概分析过,但是有一个比较重要的分支是RunnableFuture,下面专门分析:
Future的接口规范如下,后面在TaskFuture中的实现进行分析:
public interface Future<V> {
// 取消任务
boolean cancel(boolean mayInterruptIfRunning);
// 是否被取消了
boolean isCancelled();
// 是否执行完成
boolean isDone();
// 阻塞获取结果
V get() throws InterruptedException, ExecutionException;
// 带超时地阻塞获取结果
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
2、RunnableFuture
RunnableFuture的主要实现就是TaskFuture,先看看RunnableFuture的结构(实现了两个接口Future、Runnable,所以个人理解是即适配了原来的jdk1.0的Runnable,又扩展了Future):
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
3、FutureTask
public class FutureTask<V> implements RunnableFuture<V> {
/**
* The run state of this task, initially NEW. The run state
* transitions to a terminal state only in methods set,
* setException, and cancel. During completion, state may take on
* transient values of COMPLETING (while outcome is being set) or
* INTERRUPTING (only while interrupting the runner to satisfy a
* cancel(true)). Transitions from these intermediate to final
* states use cheaper ordered/lazy writes because values are unique
* and cannot be further modified.
*
* Possible state transitions:
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
*/
private volatile int state;
private static final int NEW = 0; // 创建完成(在构造器中设置)
private static final int COMPLETING = 1; // 完成
private static final int NORMAL = 2; // 正常执行完成
private static final int EXCEPTIONAL = 3; // 异常执行完成
private static final int CANCELLED = 4; // 取消
private static final int INTERRUPTING = 5; // 正在被中断
private static final int INTERRUPTED = 6; // 被中断完成了
/** The underlying callable; nulled out after running */
private Callable<V> callable;
/** Object类型,存放回调函数的真正结果,或者存放get方法的抛出的异常 */
private Object outcome; // non-volatile, protected by state reads/writes
/** The thread running the callable; CASed during run() */
private volatile Thread runner;
/** Treiber stack of waiting threads */
private volatile WaitNode waiters;
// 既然有 volatile修饰的state(解决可见性、有序性),那么CAS肯定少不了
private static final sun.misc.Unsafe UNSAFE;
}
1)、FutureTask结构(结构和可能的生命周期路径)
设置TaskFuture的生命周期状态state,并且设置了所有可能的路径,结合Future定义的接口方法,已经具体的FutureTask实现:
NEW -> COMPLETING -> NORMAL
NEW -> COMPLETING -> EXCEPTIONAL
NEW -> CANCELLED
NEW -> INTERRUPTING -> INTERRUPTED
NEW = 0; // 创建完成(在构造器中设置)
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
COMPLETING = 1; // 正在完成(在cas中完成,通过set、setException方法)
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
NORMAL = 2; // 正常执行完成(在putOrderedInt,中完成set方法【如上】)
EXCEPTIONAL = 3; // 异常执行完成(在putOrderedInt,中完成setException方法【如上】)
CANCELLED = 4; // 取消(在Cancel方法中判断完成)
INTERRUPTING = 5; // 正在被中断
也是在cancel方法中,只有在running的状态下才会去interrupt,否则就是取消【如果任务都没有执行,就没必要中断线程了】。
INTERRUPTED = 6; // 被中断完成了(在finally中,cas设置为取消的过去式)
线程的中断问题可以参见:并发编程模式 - Two-Phase Termination两阶段终止模式
volatile WaitNode waiters; 属性定义的下一个节点的waiters,可以理解成链表,只是链表的key为线程,结构如下:
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
在哪里处理设置的呢,finishCompletion中 WaitNode next = q.next; 后面分析。
2)、FutureTask实现原理(LockSupport的park、parkNanos + 自旋、unpark实现的等待唤醒机制)
get
Future定义的get()方法就是阻塞获取结果、get(time, TimeUnit)带超时地阻塞获取结果,其实现原理可能不陌生就是等待唤醒机制。等待唤醒机制使用 synchronized + Object类的 wait、notify(notifyAll)方法可以实现;之前在分析Dubbo的DefaultFuture的get方法,异步转同步的过程(并发编程模式 - Guarded Suspension设计模式);而当前使用LockSupport的park、unpark方法实现。
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING) // 状态小于等待完成,即New或者COMPLETING
s = awaitDone(false, 0L);
// 最终返回结果
return report(s);
}
report比较简单,处理异常,执行返回 Object类型的outcome属性:
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
awaitDone
// 不带超时,传入的参数为: false, 0L
private int awaitDone(boolean timed, long nanos) throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {
// 自旋中,判断是否被中断,中断则移除该 NodeWait节点
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
// 判断状态大于正在完成, 那么也肯定是正常或者异常完成、或者中断
int s = state;
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
// 状态为正在完成,则让渡当前的线程的CPU使用权,当前线程状态从 【运行】 切换为 【可运行】
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
// 如果当前线程还没有创建节点,则创建节点并添加到“链表”中去,表明当前线程也准备进入等待该任务完成的队列中去
else if (q == null)
q = new WaitNode();
// queued第一次进入为false,则会进入该判断,在cas中将上一步创建的节点添加到链表中
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
// 最后如果自旋时没有超时,则将线程挂起(LockSupport的park或者parkNanos),等待其他操作的 LockSupport#unpark(当前挂起的线程)
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
LockSupport.park(this);
}
}
我们找一下发现只要 finishCompletion方法【自旋,将节点cas设置为null】中会调用 LockSupport#unpark(线程):
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
// 留给子类实现的空方法,当前类为空(比如ExecutorCompletionService就有实现扩展)
done();
callable = null; // to reduce footprint
}
调用finishCompletion的方法有:cancel、set、setException,刚好与上面的state状态的切换相对应。
我们就看正常流程的,调用方法为TaskFuture的run方法。之前分析了FutureTask是Future和Runnable的实现,那么只要该Runnable调用了start方法,就会等待被CPU切中,执行run方法中的回调函数,如下:
public void run() {
// 状态不是刚创建 或者使用当前线程去控制处理该节点,则返回
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
//该Callable回调函数不为null,并且状态还是New状态
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 执行回调方法,
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
// 设置异常,最终会调用 LockSupport#unpark
setException(ex);
}
if (ran)
// 正常执行完 最终会调用 LockSupport#unpark
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
至此,整个流程就全梳理通了...