本篇主要介绍FutureTask源码
我们知道FutureTask实现了RunnableFuture接口,即Runnable接口和Future接口,Runable可以对应FutureTask的task,表示FutureTask本质上也是一个task任务,而Future对应FutureTask中的Future,表示了我们对于这个task任务可以执行某些操作,如判断任务是否执行完毕,获取任务的执行结果,取消任务的执行等等。
状态
FutureTask中,状态由state属性来表示,它是volatile类型的,保证了某个线程对它的改变其它线程可见。
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;
state属性是贯穿整个FutureTask的最核心的属性,该属性的值代表了任务在运行过程中的状态,随着任务的执行,状态将不断地进行转变,从上面的定义中可以看出,总共有7种状态:包括了1个初始态,2个中间态和4个终止态。
注意:任务的中间状态是一个瞬态,它非常的短暂。而且任务的中间态并不代表任务正在执行,而是任务已经执行完了,正在设置最终的返回结果。也可以这么说,只要state不处于NEW
状态,就说明任务已经执行完毕。
这里的执行完毕是指传入的Callable对象的call方法执行完毕,或者抛出了异常。所以这里的COMPLETING
的名字显得有点迷惑性,它并不意味着任务正在执行中,而意味着call方法已经执行完毕,正在设置任务执行的结果。
而将一个任务的状态设置成终止态只有三种方法:set setException cancel
队列
FutureTask中,队列的实现是一个单向链表,它表示所有等待任务执行完毕的线程的集合。我们知道,FutureTask实现了Future接口,可以获取Task的执行结果,那么如果获取结果时,任务还没有执行完毕怎么办呢?那么获取结果的线程就会在一个等待队列中挂起,直到任务执行完毕被唤醒。这一点有点类似于我们之前学习的AQS中的sync queue。
包装成某种类型的数据结构扔到等待队列中,然后等待被唤醒,这里也不例外,FutureTask中结点结构如下
在AQS源码分析中,我们知道对于挂起的线程,通常会被
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
注意:F
utureTask中的这个单向链表是当做栈来使用的,确切来说是当做Treiber栈来使用的,它是一个线程安全的栈,使用CAS来完成入栈出栈操作,为什么要使用一个线程安全的栈呢,因为同一时刻可能有多个线程都在获取任务的执行结果,如果任务还在执行过程中,则这些线程就要被包装成WaitNode
扔到Treiber栈的栈顶,即完成入栈操作,这样就有可能出现多个线程同时入栈的情况,因此需要使用CAS操作保证入栈的线程安全,对于出栈的情况也是同理。
由于FutureTask中的队列本质上是一个Treiber栈,那么使用这个队列就只需要一个指向栈顶节点的指针就行了,在FutureTask中,就是waiters
属性,事实上,它就是整个单向链表的头节点
/** Treiber stack of waiting threads */
private volatile WaitNode waiters;
FutureTask队列结构如下
CAS操作
CAS操作大多数是用来改变状态的,在FutureTask中也不例外。我们一般在静态代码块中初始化需要CAS操作的属性的偏移量
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long stateOffset;
private static final long runnerOffset;
private static final long waitersOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> k = FutureTask.class;
stateOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("state"));
runnerOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("runner"));
waitersOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("waiters"));
} catch (Exception e) {
throw new Error(e);
}
}
从这个静态代码块中我们也可以看出,CAS操作主要针对3个属性,包括state
、runner
和waiters
,说明这3个属性基本是会被多个线程同时访问的。其中state
属性代表了任务的状态,waiters
属性代表了指向栈顶节点的指针,这两个我们上面已经分析过了。runner
属性代表了执行FutureTask中的“Task”的线程。为什么需要一个属性来记录执行任务的线程呢?这是为了中断或者取消任务做准备的,只有知道了执行任务的线程是谁,我们才能去中断它。
定义完属性的偏移量之后,接下来就是CAS操作本身了。在FutureTask,CAS操作最终调用的还是Unsafe类的compareAndSwapXXX
方法。
核心属性
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;
/** The result to return or exception to throw from 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;
关于state
、waiters
、runner
三个属性我们上面已经解释过了。剩下的callable
属性代表了要执行的任务本身,即FutureTask中的Task部分,为Callable类型,这里之所以用Callable而不用Runnable是因为FutureTask实现了Future接口,需要获取任务的执行结果。outcome
属性代表了任务的执行结果或者抛出的异常,为Object类型,也就是说outcome
可以是任意类型的对象,所以当我们将正常的执行结果返回给调用者时,需要进行强制类型转换,返回由Callable定义的V
类型。这5个属性综合起来就完成了整个FutureTask的工作。
构造函数
// FutureTask构建初始态为NEW
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW;
}
// 如果参数是Runnable,通过适配器适配成Callable了类型,这里上一篇讲过。
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW;
}
接口实现
public class FutureTask<V> implements RunnableFuture<V> {
...
}
①Runnable接口实现
FutureTask的run方法实现如下
public void run() {
// 如果任务状态不为NEW,表明任务已经启动或结束,直接返回
// 任务未启动,未初始态,但是cas更新执行当前任务的线程失败,直接返回
if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) { // 执行出现异常
result = null;
ran = false;
setException(ex); // 设置错误信息,赋值outcome
}
if (ran) // 执行正常
set(result); // 设置正常输出结果,赋值outcome
}
} 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) // 此时state为INTERRUPTING,INTERRUPTED
handlePossibleCancellationInterrupt(s);
}
}
protected void set(V v) {
// 此处state先更新为COMPLETING中间状态,COMPLETING是一个非常短暂的中间态,表示正在设置执行的结果
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
// 由于state属性被设置成volatil, putOrderedInt等价于putIntVolatile
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
// 此处不能判断state是COMPLETING中间态还是INTERRUPTING中间态,因为在多线程的环境中,在当前线程执行run方法的同时,有可能其他线程取消了任务的执行,此时其他线程就可能对state状态进行改写,所以此处不用cas。
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
private void finishCompletion() {
// assert state > COMPLETING;
// 前面讲FutureTask队列结构是提过,它是栈结构,而waiters相当于栈顶指针
for (WaitNode q; (q = waiters) != null;) {
// 设置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;
}
}
done(); // 提供给子类实现
callable = null; // 断开callable的引用
}
//该方法是一个自旋操作,如果当前的state状态是INTERRUPTING,我们在原地自旋,直到state状态转换成终止态
private void handlePossibleCancellationInterrupt(int s) {
// It is possible for our interrupter to stall before getting a
// chance to interrupt us. Let's spin-wait patiently.
if (s == INTERRUPTING)
while (state == INTERRUPTING)
Thread.yield(); // wait out pending interrupt
}
②Future接口的实现
cancel(boolean mayInterruptIfRunning)取消一个任务的执行
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW && UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}
首先有以下三种情况之一的,cancel操作一定是失败的:
1.任务已经执行完成了
2.任务已经被取消过了
3.任务因为某种原因不能被取消
其它情况下,cancel操作将返回true。值得注意的是,cancel操作返回true并不代表任务真的就是被取消了,这取决于发动cancel状态时,任务所处的状态:
1.如果发起cancel时任务还没有开始运行,则随后任务就不会被执行;
2.如果发起cancel时任务已经在运行了,则这时就需要看mayInterruptIfRunning
参数了:
如果mayInterruptIfRunning
为true, 则当前在执行的任务会被中断
如果mayInterruptIfRunning
为false, 则可以允许正在执行的任务继续运行,直到它执行完
isCancelled()
public boolean isCancelled() {
return state >= CANCELLED;
}
isDone()
public boolean isDone() {
return state != NEW;
}
get()(超时版本get()类似)
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING) // 此处s=NEW、COMPLETING
s = awaitDone(false, 0L);
return report(s);
}
private int awaitDone(boolean timed, long nanos) throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {
if (Thread.interrupted()) { // 等待结果线程被中断
removeWaiter(q); // 删除结点
throw new InterruptedException();
}
int s = state;
if (s > COMPLETING) { // state = 2,3,4,5,6
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) // cannot time out yet
Thread.yield(); // 让出cpu,等待任务state状态为终态
else if (q == null)
q = new WaitNode(); // 首次进入线程包装成waitNode
else if (!queued) // 入对,这里是在栈顶
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) { // 如果已经到期了,删除等待结点
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos); // 超时阻塞
}
else
LockSupport.park(this); // 阻塞
}
}
private void removeWaiter(WaitNode node) {
if (node != null) {
node.thread = null; //将需要去除的节点,thread赋值为null
retry:
for (;;) {
for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
s = q.next;
if (q.thread != null)
pred = q;
else if (pred != null) { //q.thread==null,表示该节点是需要在队列中去除的节点,故直接将pred.next=s,重组队列
pred.next = s;
if (pred.thread == null) //如果这个pred节点恰好是需要去除的节点,则进行循环,重组队列
continue retry;
}
// 如果要删除的node是栈顶元素,则cas为node的下一个结点
else if (!UNSAFE.compareAndSwapObject(this, waitersOffset, q, s))
continue retry;
}
break;
}
}
}
在具体分析它的源码之前,有一点我们先特别说明一下,FutureTask中会涉及到两类线程,一类是执行任务的线程,它只有一个,FutureTask的run方法就由该线程来执行;一类是获取任务执行结果的线程,它可以有多个,这些线程可以并发执行,每一个线程都是独立的,都可以调用get方法来获取任务的执行结果。如果任务还没有执行完,则这些线程就需要进入Treiber栈中挂起,直到任务执行结束,或者等待的线程自身被中断。
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL) // state为正常状态,直接返回
return (V)x;
if (s >= CANCELLED) // CANCELLED,INTERRUPTING,INTERRUPTED时抛出异常
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
来源:CSDN
作者:黑洞刺客
链接:https://blog.csdn.net/jiangtianjiao/article/details/104031415