目录
Future模式
- 在Java中如果要执行一个任务,一般要实现Runnable接口:
public class MyTask implements Runnable {
@Override
public void run() {
}
}
- 但是如果需要得到任务执行的结果,则需要继承Callable接口:
public class MyTask implements Callable {
@Override
public Object call() {
}
}
- 背景:一般我们调用一个函数,只有等待函数执行结束后,才能继续往下执行;但是如果函数的执行时间较长,那么我们会使用异步任务的形式,创建一个新的线程进行调用。但是这种方式我们将不能得到其执行结果。
Future模式用于 异步 地获取任务的 执行结果 。
- Future模式让调用方调用任务执行之后拿到一个 凭证(Future) ,然后立即返回;
- 调用者可以先去执行其他的任务;
- 在需要的时候,根据凭证去获取任务的执行结果。
Future接口、FutureTask类
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;
}
- isCancelled:监控任务是否被取消
- isDone:监控任是否执行结束
- cancel:取消任务的执行
- get:用于获取任务执行结果,如果调用时还没执行结束,将会阻塞(除非设置超时时间)
J.U.C为我们提供了Future接口的实现类:FutureTask。
使用Demo
创建任务,demo中是Runnable类型任务无返回值,也可以创建Callable带返回值的任务:
class MyRunnableTask implements Runnable {
@Override
public void run() {
try {
//任务执行耗时
System.out.println("任务开始执行");
Thread.sleep(3000);
System.out.println("任务执行结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
有两种形式使用Future的方式:
- 直接使用Thread与FutureTask:
public class TestFuture {
public static void main(String[] args) {
MyRunnableTask myRunnableTask = new MyRunnableTask();
FutureTask future = new FutureTask(myRunnableTask, null);
Thread thread = new Thread(future);
thread.start();
System.out.println("main线程继续执行");
try {
future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("main线程执行结束");
}
}
- 根据ExecutorService的submit方法:
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(1);
MyRunnableTask myRunnableTask = new MyRunnableTask();
Future future = executorService.submit(myRunnableTask);
executorService.shutdown();
try {
future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
看submit方法内部,其实也是创建了一个FutureTask进行返回:
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
Future接口实现类——FutureTask
1. 类继承关系
可知FutureTask是Future接口的实现类,另外还继承了Runnable接口,所以其本质是一个异步的任务。
- 一方面是其Task的性质:我们可以将其提交给ThreadPoolExecutor执行,也可以直接调用run方法执行;
- 另一方面是其Future的性质:我们可以对这个任务进行额外的操作,监控是否执行结束、获取执行结果等。
2. 存储结构与构造函数
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;
从JDK1.8开始,FutureTask使用volatile+CAS来实现同步,之前是实现了AQS框架。
- state:任务状态值,volatile线程可见
- callable:Callable类型的任务
- outcome:调用get方法得到的返回值
- runner:执行callable任务的Thread线程
- waiters:一个链表结构,存储等待的线程
WaitNode这个单向链表,在FutureTask中被当做线程安全的栈来使用,针对于多个线程同时入栈的情况(多个线程同时调用get方法)使用CAS保证入栈的线程安全,出栈也同理。
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
此外还有一系列的UnSafe值记录:
// 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);
}
}
构造函数:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
- 可见无论是Runnable类型还是Callable类型的任务都可以创建,它们最终都会被封装为Callable;
- 任务的初始状态设为NEW
任务状态
- NEW:表示任务的初始化状态;
- COMPLETING:表示任务已执行完成(正常完成或异常完成),但任务结果或异常原因还未设置完成,属于中间状态;
- NORMAL:表示任务已经执行完成(正常完成),且任务结果已设置完成,属于最终状态;
- EXCEPTIONAL:表示任务已经执行完成(异常完成),且任务异常已设置完成,属于最终状态;
- CANCELLED:表示任务还没开始执行就被取消(非中断方式),属于最终状态;
- INTERRUPTING:表示任务还没开始执行就被取消(中断方式),正式被中断前的过渡状态,属于中间状态;
- INTERRUPTED:表示任务还没开始执行就被取消(中断方式),且已被中断,属于最终状态。
可能的状态流转:
- NEW -> COMPLETING -> NORMAL
- NEW -> COMPLETING -> EXCEPTIONAL
- NEW -> CANCELLED
- NEW -> INTERRUPTING -> INTERRUPTED
- 注意COMPLETING是已执行结束的状态,只是还未设置返回的结果。
- 也就是说只要不是NEW状态,任务都已执行完了
3.结果获取
先看get方法:
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
- 如果任务的状态<COMPLETING,代表任务还未执行结束,调用awaitDone
- 调用report返回结果
3.1 awaitDone阻塞线程,返回任务状态
任务未执行完,将调用get的线程加入阻塞队列,挂起线程
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) {
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
else if (q == null)
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);
}
}
首先区分两种线程的概念:
- 一类是执行任务的线程(只有一个)执行FutureTask的run方法;
- 一类是获取任务执行结果的线程(可以有多个,可并发执行)调用get方法来获取任务的执行结果。
如果任务还没有执行完,则这些线程就需要进入线程安全的栈(也就是等待队列)中挂起,直到任务执行结束,或者等待的线程自身被中断。
再看其流程, 整体框架是一个自旋操作;
- 先检查当前线程是否被中断,如果被中断,removeWaiter将node从等待队列中移除
- 检查任务状态,如果这时已经进入终止状态,返回其状态;
- 如果处于COMPLETING执行完毕,还未设置结果完成的状态,Thread.yield(),让出当前线程的cpu继续等待;
- 否则任务正在执行中,创建一个节点q,并加入到链表的头结点之前,即入栈操作;
- 最后LockSupport.park将线程挂起。
自旋操作一直到线程被中断,或者是此线程再任务执行结束之后,finishCompletion方法中被唤醒。
3.2 report根据任务状态返回结果
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);
}
- 如果状态为正常,返回结果;
- 状态为被取消、或有异常,返回相应的异常;
4.任务执行
public void run() {
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);
}
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);
}
}
- 通过CAS将runner设置为当前线程,记录执行任务的线程;
- 调用call方法执行任务;
- 执行任务成功,set(result)设置结果;执行失败,setException(ex)返回异常;
- 执行成功:set(result)
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
- 将stats由NEW改为COMPLETING
- 给outcome赋值;
- 将state修改为NORMAL最终态
- finishCompletion完成执行结果的设置
- 执行失败:setException(ex)
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
- 将stats由NEW改为COMPLETING
- 给outcome赋值为Throwable异常;
- 将state修改为EXCEPTIONAL最终态
- finishCompletion完成执行结果的设置
finishCompletion处理等待线程
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;
}
}
done();
callable = null; // to reduce footprint
}
- 将waiters的值设为null,目的就是清空整个栈;
- 在for循环中遍历整个单向链表,LockSupport.unpark唤起等待线程;