FutureTask
1. Future
future接口定义了获取任务线程执行信息以及取消线程运行的接口方法
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接口继承了Runable接口和Future接口
- Runable接口使得RunnableFuture具有了通过Thread类执行的能力
- Future接口使得其具有可获取任务信息的能力
更像一个适配器,对Runable接口进行了适配,同时还可以获取执行结果
public interface RunnableFuture<V> extends Runnable, Future<V> {
//继承自Runnbale
void run();
}
3. FutureTask
3.1 重要的属性
//任务执行的状态
/* 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;//已被打断
//需要执行的任务
private Callable<V> callable;
//运行结果
private Object outcome; // non-volatile,
//执行任务的线程
private volatile Thread runner;
//等待获取结果的线程队列(链表)
private volatile WaitNode waiters;
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
3.2 重要的方法
3.2.1 新建任务
//构造方法
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;//设置需要执行的任务
this.state = NEW;//设置任务执行的状态为新建状态
}
3.2.2 执行任务
因为FutureTask类实现了Runnable接口,因此FutureTask可以当作Thread的构造参数,当执行Thread执行start方法,其会自动执行FutureTask的run方法
run方法
public void run() {
//state != NEW 验证任务状态是否是new状态
//UNSAFE.compareAndSwapObject(this, runnerOffset,null, Thread.currentThread())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();//直接调用callble的call方法,获取执行结果
ran = true;//设置执行成功
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);//如果出现异常,则设置状态为exception状态,并唤醒等待结果的线程
}
if (ran)
set(result);//如果运行成功,则设置结果
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
set方法设置运行结果
protected void set(V v) {
//cas设置状态为 执行完成COMPLETING
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;//设置结果
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // 设置最终状态,已经设置结果
finishCompletion();//任务完成(包括出现异常)的处理
}
}
/**
这里 putOrderedInt 是一个轻量级的操作,因为state是一个volatile修饰的
volatile能够使得变量保持线程间的可见性,其原理是加上了内存屏障
+ 保证写volatile变量会强制把CPU写缓存区的数据刷新到内存
+ 读volatile变量时,使缓存失效,强制从内存中读取最新的值
+ 由于内存屏障的存在,volatile变量还能阻止重排序
它实际上是load 和store的组合,在写入前后读入前后添加load和store的组合指令。
其中:storeload是最重的,用来保证 写变量与读变量不进行重排序,同时保证可见性
但是有时候不需要让共享变量的修改立刻让其他线程可见的时候,所以可以使用store-store的方式只保证上次写与当前写不发生重排序,只是以设置普通变量的方式来修改共享状态,可以减少不必要的内存屏障,从而提高程序执行的效率。
*/
**finishCompletion方法 ** 任务完成(包括出现异常)的处理
主要是等待着的唤醒
private void finishCompletion() {
//自旋 进行 cas设置等待队列为null
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);//如果等待队列不为null则唤醒队列头对应的线程
}
//遍历下一个等待者线程
WaitNode next = q.next;
if (next == null)
break;
q.next = null;//help gc
q = next;
}
break;
}
//else 如果不成功则自旋,重新cas
}
done();
callable = null; // to reduce footprint
}
3.2.3 获取结果
get方法
public V get() throws InterruptedException, ExecutionException {
int s = state;//获取当前的状态
if (s <= COMPLETING)//如果小于COMPLETING 说明任务为完成,需要进行阻塞
s = awaitDone(false, 0L);
//report会根据执行任务的状态确定是返回结果还是抛出异常
return report(s);
}
awaitDone(boolean timed, long nanos) 阻塞直到 任务执行完成
//timd nanos参数:如果timd为true,则最大能忍受nanos时间的阻塞,即使获取不到也会被唤醒
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();
}
/*
根据不同的状态 做不同的对待:
1.当state > COMPLETING:说明任务已经执行完成(包括结果已经设置、出现异常、打断以及取消等情况)
+ 返回即可
2.当state == COMPLETING:说明任务虽然已经执行完成,但是还未设置结果
+ 已经快完成了,可以利用Thread.yield()让出CPU,使对应的线程快速执行完。进入下一次循环(自旋)
3.当state < COMPLETING && q==null 表示还未为自己创建WaitNode
+ 为自己创建WaitNode,进入下一次循环(自旋)
4.已经创建WaitNode,还未排队
+ 将自己插入到队列中,头插;进入下一次循环(自旋)
5.已经进行了排队,需要限期获取结果
计算时间是否到期:
+ 如果到期,则移除等待队列上的WaitNode
+ 没到期:LockSupport.parkNanos(this, nanos);带有阻塞时间的阻塞
6.已经进行了排队,无限期获取结果
+ 阻塞
注意:以上6中情况,每第i中情况一定不符合前i-1中情况,设计者通过自旋来尽量的减少阻塞带来的开销,但是最多自旋3次(在任务比较重未被完成的情况下),就陷入阻塞或者返回。
*/
int s = state;
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) // cannot time out yet
Thread.yield();//让出cpu,让其他线程执行,尽量让任务的结果在这段时间内被设置
else if (q == null)
q = new WaitNode();
else if (!queued)//cas插入队列
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);
}
}