FutureTask简介
-
FutureTask实现了RunnableFuture接口,而RunnableFuture继承了Runnable和Future
-
Runnable接口,可以让FutureTask由线程池执行,也可以直接用线程调用执行
-
Future接口,可以查询任务是否完成,取消任务以及获取结果等操作
-
FutureTask可用于线程池提交任务,任务会被包装成FutureTask,其他线程可以在线程池执行 过程中查询任务状态进而获取结果或者取消任务
FutureTask属性及构造方法
-
为什么需要runner属性呢? 当需要取消任务时,会给正在执行任务的线程一个中断信号
-
WaitNode:Treiber stack,使用CAS操作栈顶来实现入栈和出栈,实际上是在操作单链表头结点
//当前任务状态
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;
//真正的任务
private Callable callable;
//正常情况下:保存任务执行结果
//非正常情况:保存任务抛出的异常
private Object outcome; // non-volatile, protected by state reads/writes
//当前任务执行期间,保存当前执行任务的线程对象引用
private volatile Thread runner;
//使用了WaitNode来保存等待get()的线程
private volatile WaitNode waiters;
public FutureTask(Callable callable) {
if (callable == null)
throw new NullPointerException();
//callable就是任务
this.callable = callable;
//设置任务状态为NEW
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
//使用装饰者模式将runnable接口转换为callable接口
//当前任务执行结果为result
this.callable = Executors.callable(runnable, result);
this.state = NEW;
}
//thread保存当前等待结果的线程
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
UNSAFE
-
compareAndSwapObject(Object var1, long var2, Object var4, Object var5)
-
var1参数是操作的对象,var2参数就是操作字段的偏移量,var4参数就是原值,var5参数就是期望值
-
这里拿到state, runner, waiters这些字段的偏移量是为了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);
}
}
解析run()
- run()方法是FutureTask被线程执行的入口
//任务执行入口
public void run() {
//条件一:true:说明当前task已经执行过了,或者被cancel,总之非NEW状态
//条件二:true:CAS设置runner为当前线程失败,当前任务被其他线程抢占
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
//当前task为new状态,当前线程抢占成功
try {
//当前任务
Callable c = callable;
//条件一:防止提交空任务
//条件二:防止其他线程已经cancel当前任务
if (c != null && state == NEW) {
//结果
V result;
//true:执行成功
//false:抛出异常
boolean ran;
try {
//执行任务
result = c.call();
//正常执行
ran = true;
} catch (Throwable ex) {
//抛出异常,结果null
result = null;
ran = false;
//设置异常
setException(ex);
}
//执行成功,设置结果
if (ran)
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);
}
}
解析set() 和 setException()
-
任务执行完成后会调用set()方法设置outcome,出现异常调用setException()设置outcome
-
会用CAS操作修改任务状态,有可能设置失败,这是因为有其他线程取消了任务
//设置执行结果
protected void set(V v) {
//使用CAS设置当前任务状态为 完成中
//失败情况:外部线程取消了任务
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
//设置结果
outcome = v;
//设置当前任务状态为正常完成
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
//唤醒阻塞在get()线程,下面会写
finishCompletion();
}
}
//执行出现异常时,执行此方法设置异常
protected void setException(Throwable t) {
//使用CAS设置当前任务状态为 完成中
//失败情况:外部线程取消了任务
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
//设置结果,结果为异常
outcome = t;
//设置当前任务状态为异常状态
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
//唤醒阻塞在get()线程,下面会写
finishCompletion();
}
}
解析get() 和 report()
- get()用来获取任务执行的结果,当任务为未执行状态或者正在执行状态时会阻塞在awaitDone()方法
//场景:可能有多个线程等待结果
public V get() throws InterruptedException, ExecutionException {
int s = state;
//条件成立:未执行状态,正在执行状态
if (s <= COMPLETING)
//返回状态,可能线程已经睡了一会
s = awaitDone(false, 0L);
//抛出异常或者正常结果
return report(s);
}
//get()调用这个方法
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() 和 removeWaiter()
-
这里为了理解方便把Treiber stack理解成一个队列
-
awaitDone()方法会把当前线程封装成WaitNode对象,放入队列中并阻塞
-
removeWaiter()方法把结点移除队列
//get()方法可能调用
private int awaitDone(boolean timed, long nanos) throws InterruptedException {
//0:不带超时
final long deadline = timed ? System.nanoTime() + nanos : 0L;
//用来封装当前线程的WaitNode对象
WaitNode q = null;
//当前线程 WaitNode对象是否入队
boolean queued = false;
//自旋
for (;;) {
//条件成立:当前线程是被其他线程使用中断唤醒
//Thread.interrupted():判断中断状态,会把中断标记重置为false
if (Thread.interrupted()) {
//出队
removeWaiter(q);
//抛出中断异常
throw new InterruptedException();
}
//被正常唤醒,获取最新状态
int s = state;
//说明已经有结果,可能好可能坏
if (s > COMPLETING) {
//条件成立:说明线程创建过node
if (q != null)
q.thread = null;
//返回结果
return s;
}
//说明任务接近完成,看前面就知道马上就会把状态设置为已完成
else if (s == COMPLETING) // cannot time out yet
//暂时放弃CPU
Thread.yield();
//条件成立:第一次自旋,当前线程还未封装成WaitNode
else if (q == null)
//把当前线程封装成WaitNode
q = new WaitNode();
//条件成立:第二次自旋,线程已经封装成WaitNode,但是还未入队
else if (!queued)
//q.next = waiters:将当前线程的node结点next指向队头结点
//通过CAS设置队头waiters为当前线程的node结点q
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
//park当前线程
//等待其他线程中断或者唤醒当前线程
LockSupport.park(this);
}
}
//出队操作
private void removeWaiter(WaitNode node) {
if (node != null) {
//将node中的thread引用置为空
node.thread = null;
retry:
for (;;) { // restart on removeWaiter race
//pred:q的前置结点 q:从队头开始遍历 s:q的后置结点
for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
s = q.next;
//当前结点thread不为空
if (q.thread != null)
pred = q;
//当前结点thread为空,需要出队 //前置结点不为空
else if (pred != null) {
//直接指向后置结点
pred.next = s;
//前置结点对应的线程可能会被唤醒,也出队,继续自旋
if (pred.thread == null) // check for race
continue retry;
}
//当前结点thread为空,前置结点也为为空:也就是出队结点为头结点
//cas设置头结点
else if (!UNSAFE.compareAndSwapObject(this, waitersOffset, q, s))
continue retry;
}
break;
}
}
}
解析cancel()
-
cancel()的前提是当前任务状态为NEW
-
cancel()方法可以直接取消当前任务,也可以给当前正在执行任务的线程发送一个中断信号
//参数true:如果有执行的线程,给它发一个中断信号
//false:直接取消任务
public boolean cancel(boolean mayInterruptIfRunning) {
//条件一:state == NEW 当前任务还未执行
//条件二:CAS 设置状态
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;
}
解析finishCompletion()
-
finishCompletion()方法是在任务执行完set方法设置了outcome后或者有线程cancel()后调用该方法
-
finishCompletion()方法用来将阻塞的线程一一出队,并唤醒
private void finishCompletion() {
// assert state > COMPLETING;
//把q指向头结点
for (WaitNode q; (q = waiters) != null;) {
//使用CAS设置waiters为null
//失败情况:cancel()方法也会指向finishCompletion()这个方法
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;
//GC
q.next = null; // unlink to help gc
//指向下一个结点
q = next;
}
break;
}
}
//可以扩展done()
done();
//释放任务
callable = null; // to reduce footprint
}
总结
- 下图是线程可能的状态转换图
-
下图总结了几种可能存在的情况,可以结合源码走一下流程
-
比如线程A正在执行任务,线程B和线程C等待获取结果被阻塞,随后线程C被中断,这时线程C会把自己从Treiber stack中移除,然后抛出中断异常
-
假如线程D取消任务,线程A在设置结果时,会发现状态不对,设置失败,线程D会唤醒线程B,这时线程B会抛出取消异常
-
假如线程A顺利执行任务完成并设置结果,会唤醒线程B,这时线程B会拿到正常的结果