FutureTask
概述
FutureTask实现了RunnableFuture接口,而RunnableFuture接口继承了Runnable和Future接口。
FutureTask可以作为任务放到线程池中执行。
FutureTask还提供了一系列的方法,用于获取任务的执行结果,判断任务是否执行结束,以及取消任务。
FutureTask应用
板书栏:
public class Test {
static ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
public static void main(String[] args) throws InterruptedException {
FutureTask<String> futureTask = new FutureTask<>(() -> {
System.out.println("任务开始执行");
Thread.sleep(1000);
System.out.println("任务执行结束");
return "OK";
});
executor.execute(futureTask);
// isDone方法判断任务是否执行结束
// System.out.println("任务执行结束了吗:" + futureTask.isDone());
// Thread.sleep(3000);
// System.out.println("任务执行结束了吗:" + futureTask.isDone());
// 获取任务的返回结果
// try {
// // 一直等待
// String result = futureTask.get();
// System.out.println("任务的返回结果:" + result);
// } catch (ExecutionException e) {
// e.printStackTrace();
// }
//
// try {
// // 只等待1秒
// String result = futureTask.get(1000, TimeUnit.SECONDS);
// System.out.println("任务的返回结果:" + result);
// } catch (ExecutionException e) {
// e.printStackTrace();
// } catch (TimeoutException e) {
// // 如果等待时间过了,还没有返回结果,返回TimeoutException异常
// System.out.println("等待超时");
// e.printStackTrace();
// }
// futureTask任务是有任务状态的
// 任务被执行过后,状态发生变化,再次调用run方法任务是不会执行的
futureTask.run();
executor.shutdown();
}
}
要点栏:
要点1:
isDone方法是获取任务是否执行结束的状态
要点2:
无参get方法会一直等待任务结果。有参get方法是有一个时间限制,如果超过等待时间没有返回结果,就会抛出超时异常。
要点3:
FutureTask任务是有任务状态的。任务被执行过一次后,任务状态会发生变化,这个任务就不会再被执行了,除非调用runAndReset方法重置任务状态。
FutureTask源码
FutureTask基本属性
板书栏:
public class FutureTask<V> implements RunnableFuture<V> {
/**
* 任务的几种状态转变过程
* NEW -> COMPLETING -> NORMAL 任务正常结束,结果正常返回的过程
* NEW -> COMPLETING -> EXCEPTIONAL 任务正常结束,结果返回异常的过程
* NEW -> CANCELLED 任务被取消
* NEW -> INTERRUPTING -> INTERRUPTED 任务被中断
*/
// 存储任务的状态
private volatile int state;
// 初始化FutureTask对象时,任务的默认状态
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;
// 执行任务的线程
private volatile Thread runner;
// 等待任务返回结果的线程Node对象
private volatile WaitNode waiters;
}
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
要点栏:
要点1:
任务是有四种状态变化过程的:
- 任务正常结束,结果正常返回的过程
- 任务正常结束,结果返回异常的过程
- 任务被取消
- 任务被中断
要点2:
等待返回结果的线程都被封装成了WaitNode对象,并且被挂起。当任务执行结束返回结果时,所有线程都被唤醒。
FutureTask基本方法
run方法
板书栏:
public void run() {
if (
// 判断任务状态是否是NEW,如果不是,直接返回不执行
state != NEW ||
// 将runner属性修改成当前线程,如果修改失败,直接返回不执行任务
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
// c赋值为任务
Callable<V> c = callable;
// 任务不为空的健壮性判断
// 判断任务状态是否为NEW
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 执行任务
result = c.call();
// 任务执行成功,ran赋值为true
ran = true;
} catch (Throwable ex) {
// 任务执行过程中抛出异常
// result赋值为null
result = null;
// ran赋值为false
ran = false;
// 封装异常的结果
setException(ex);
}
if (ran)
// 封装正常的结果
set(result);
}
} finally {
// 当前线程已经执行完任务
// runner赋值为null
runner = null;
int s = state;
// 判断任务状态是否大于等于INTERRUPTING
if (s >= INTERRUPTING)
// 进入这里说明任务被中断,做相应的处理
handlePossibleCancellationInterrupt(s);
}
}
private void handlePossibleCancellationInterrupt(int s) {
if (s == INTERRUPTING)
while (state == INTERRUPTING)
Thread.yield();
}
要点栏:
要点1:
如果任务状态不是NEW,或者runner属性无法设置成当前线程,直接返回不执行任务
要点2:
异常的结果会被setException方法处理,正常的结果会被set方法处理
要点3:
如果任务执行过程中,任务被中断,需要做中断处理
set和setException方法
板书栏:
// 任务正常结束封装结果
protected void set(V v) {
// 将任务状态从NEW改成COMPLETING
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
// 将任务正常结果赋值给outcome
outcome = v;
// 将任务状态改成NORMAL
UNSAFE.putOrderedInt(this, stateOffset, NORMAL);
finishCompletion();
}
}
// 任务异常结束封装结果
protected void setException(Throwable t) {
// 将任务状态从NEW改成COMPLETING
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
// 将任务异常结果赋值给outcome
outcome = t;
// 将任务状态改成EXCEPTIONAL
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL);
finishCompletion();
}
}
要点栏:
要点1:
set方法是封装正常的任务结果,并且任务状态变化过程是:NEW->COMPLETING->NORMAL
setException是封装异常的任务结果,并且任务状态变化过程是:NEW->COMPLETING->EXCEPTIONAL
要点2:
从COMPLETING状态变成NORMAL或者EXCEPTIONAL状态,中间只有一步操作,将任务执行结果赋值给outcome
cancel方法
板书栏:
public boolean cancel(boolean mayInterruptIfRunning) {
// 判断任务状态是否是NEW,如果不是,返回false
// 如果是,基于mayInterruptIfRunning的值修改任务状态
// mayInterruptIfRunning为true:state从NEW改成INTERRUPTING
// mayInterruptIfRunning为false:state从NEW改成CANCELLED
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try {
// 判断mayInterruptIfRunning是否为true
if (mayInterruptIfRunning) {
try {
// mayInterruptIfRunning为true
// 获取执行任务的线程
Thread t = runner;
if (t != null)
// 将执行任务的线程中断标志位设置为true
t.interrupt();
} finally {
// 将任务状态设置为INTERRUPTED
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}
要点栏:
要点1:
调用cancel方法,入参为false,任务会从NEW状态变成CANCELLED状态,但是任务会正常执行结束;如果入参为true,任务会从NEW状态变成INTERRUPTING状态,最后变成INTERRUPTED状态。如果任务会响应中断,那么任务执行过程中会抛出异常,不会正常结束。
要点2:
如果任务执行完成后,执行cancel方法,cancel方法返回true
get方法
板书栏:
public V get() throws InterruptedException, ExecutionException {
int s = state;
// 判断任务状态是否为NEW或者COMPLETING
if (s <= 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
WaitNode q = null;
// 任务结束标志
boolean queued = false;
for (;;) {
// 判断线程是否中断
if (Thread.interrupted()) {
// 从链表中删除当前线程节点
removeWaiter(q);
// 抛出中断异常
throw new InterruptedException();
}
int s = state;
if (s > COMPLETING) {
// 进入这里说明任务执行结束
if (q != null)
// 将线程的WaitNode对象中thread设置为null
q.thread = null;
return s;
}
else if (s == COMPLETING)
// 任务还未结束,线程释放CPU资源等待一会
Thread.yield();
else if (q == null)
// 初始化q
q = new 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 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);
}
要点栏:
要点1:
当任务状态为NEW或者COMPLETING时,get方法会等待任务执行完成;当任务执行结束,线程被唤醒,get方法会返回任务结果
要点2:
挂起的线程都被封装成WaitNode对象,添加到链表中
finishCompletion方法
板书栏:
private void finishCompletion() {
// q设置成挂起线程中的头节点
for (WaitNode q; (q = waiters) != null;) {
// 将waiters属性设置成null
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
// t设置成节点的线程
Thread t = q.thread;
if (t != null) {
// 进入这里说明线程不为null
q.thread = null;
// 唤醒线程
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
// 进入这里说明不存在下一个节点
// 跳出for循环
break;
q.next = null;
q = next;
}
// 所有的线程都已经被唤醒
break;
}
}
// 预留的方法,等待程序猿实现
done();
// 任务执行完成,callable设置为null
callable = null;
}
要点栏:
要点1:
finishCompletion方法是在任务执行结束后,唤醒挂起的所有线程,并且将callable属性设置为null