Future/Callable demo
Future/Callable实现了一个异步执行并且带有返回值的功能。Future代表异步执行的结果,Callable代表异步执行的任务,它会将执行结果返回给Future。demo示例:
public class FutureAndCallable {
//定义了一个具有返回值的任务
static class CalculationCallable implements Callable<Integer> {
private int x;
private int y;
public CalculationCallable(int x, int y){
this.x = x;
this.y = y;
}
@Override
public Integer call() throws Exception {
System.out.println("begin call:" + new Date());
TimeUnit.SECONDS.sleep(2);
return x + y;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
CalculationCallable calculationCallable = new CalculationCallable(1,2);
//用FutureTask声明一个带有返回值的任务,把CalculationCallable当做参数传进去
FutureTask<Integer> futureTask = new FutureTask<>(calculationCallable);
//把futureTask丢给线程去执行
new Thread(futureTask).start();
System.out.println("begin execute futureTask:" +new Date());
//使用futureTask.get()获取返回值,同步阻塞方法
Integer result = futureTask.get();
System.out.println("result:"+result+"");
System.out.println("end execute futureTask:" + new Date());
}
}
Future/Callable的实现原理
上述示例中,new Thread(futureTask).start();
代表启动一个线程,也就是说会执行run()方法,但是我们知道run()方法是没有返回值的,所以返回值是由其他接口提供的。
我们查看FutureTask的源码:
核心属性
//代表任务在运行过程中的状态
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;
/** 任务的执行结果,最终通过future.get()获取的值 */
private Object outcome; // non-volatile, protected by state reads/writes
/** 当前执行callable任务的线程 */
private volatile Thread runner;
/** 单向链表,用来保存所有等待任务执行结束的线程 */
private volatile WaitNode waiters;
FutureTask.run()方法源码
public void run() {
//首先判断状态是否为new,并且利用自选操作把runner属性设置为当前线程。如果state!=NEW或者自旋操作失败直接返回,此时说明已经有任务执行
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
//拿到构造方法传入的Callable实现类
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 使用c.call()方法获取执行结果;
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
//调用set()方法将执行结果保存到outcome属性中
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(result)源码
//把调用call()方法获取的结果保存到outcome,并修改任务执行状态为NORMAL,
//最后调用finishCompletion()方法
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
FutureTask.get()源码
public V get() throws InterruptedException, ExecutionException {
int s = state;
//判断当前状态如果不是COMPLETING,就调用awaitDone()方法让当钱线程等待,知道任务完成。
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
awaitDone()方法
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
//设置阻塞超时时间。
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {//自旋操作,目的是利用CAS机制来解决多线程竞争问题。
//判断线程是否中断,如果中断则在队列中移除
if (Thread.interrupted()) {
removeWaiter(q);
//抛出InterruptedException
throw new InterruptedException();
}
int s = state;
//判断线程是否是终止状态,直接返回任务状态。
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
//如果线程正在设置执行结果则调用yield()方法让出CPU资源
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) {
//如果设置了超时事件,则调用 LockSupport.parkNanos(this, nanos);方法阻塞线程并设置阻塞时间
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
// 如果没有设置超时时间,则使用LockSupport.park(this)方法阻塞当前线程
else
LockSupport.park(this);
}
}
上述源代码中LockSupport.park(this);
阻塞了当前线程,那么此线程会在什么时候被唤醒呢?
- 任务执行完毕,在set()方法中调用
finishCompletion();
方法进行唤醒 - 线程中断,在
awaitDone()
方法中调用if (Thread.interrupted())
进行中断判断
if (Thread.interrupted())方法
当Callable任务执行完成后,在set()方法中调用finishCompletion()方法完成阻塞线程的唤醒。
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
//CAS操作将waiters的值设置为null。waiters表示所有阻塞线程构建的栈结构,设置为null相当于清空栈
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {//再次从栈顶进行遍历
Thread t = q.thread;
if (t != null) {
q.thread = null;
//如果有线程不为null的节点则调用 LockSupport.unpark(t);进行唤醒
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
}
总结:
- FutureTask实现了Runnable和Future接口,它表示一个带有状态及执行结果的任务,而任务执行结果的获取是基于阻塞的方式来实现的,也就是在call()方法没有返回结果之前,其他线程调用future.get()去获取结果时,FutureTask会构建一个Treiber栈结构,把当前线程存储到栈顶并通过LockSupport来阻塞,直到call()方法返回后把结果设置到outcome中,并唤醒被阻塞的线程。
- 在获取异步执行结果时,要么调用get()方法阻塞等待返回结果。要么通过轮询调用future.isDone()来判断任务执行状态,再调用get()方法获取结果,这会耗费CPU资源。
- Future没有提供通知机制,我们没办法知道Future什么时候执行完成。