在学习线程池之前,需要先学习一下 FutureTask,因为线程池 submit 的结果就是 FutureTask。
那么什么是 FutureTask,其表示一个未来的任务。也就是说这个任务比较耗时,当前调用线程会阻塞拿到这个结果。
FutureTask 接口继承体系

FutureTask 实现了 Future 和 Runnable 接口,又能执行,又能拿到执行结果。
所有的通过 submit 方式提交到线程池的任务(Runnable、Callable),进入到队列或被线程执行之前,都会被封装成 FutureTask,然后提交到线程池中。
Future 接口表示异步计算的结果,通过 Future 接口提供的方法,可以很方便的查询异步计算任务是否执行完成,获取异步计算的结果,取消未执行的异步任务,或者中断异步任务的执行,接口定义如下:
public interface Future {boolean cancel(boolean mayInterruptIfRunning);boolean isCancelled();boolean isDone();V get() throws InterruptedException, ExecutionException;V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
}
Runnable 接口,就不做过多的解释了。
run 方法或者放入线程池执行,之后可以在外部通过 FutureTask 的 get 方法异步获取执行结果,因此,FutureTask非常适合用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。另外,FutureTask 还可以确保即使调用了多次 run 方法,它都只会执行一次 Runnable 或者 Callable 任务,或者通过 cancel 取消 FutureTask 的执行等。在 JDK1.5 时,增加了一种开启线程的方式,实现 Callable 接口,这种开启线程的方式可以拿到返回值并且可以捕获异常。但是我们必须配合着FutureTask 类使用。FutureTask 类上就是 Runnable 接口的一个实现类。因为最终开启线程还是得通过new Thread().start()的方式开启线程。
简单的线程池案例
public class FutureTaskDemo {public static void main(String[] args) throws Exception{ExecutorService threadPool = Executors.newFixedThreadPool(5);Future> future = threadPool.submit(new Runnable() {@Overridepublic void run() {System.out.println("FutureTaskDemo.run");}});future.get();}
}
我们可以看到线程池返回的 Future 是 FutureTask:

public static void main(String[] args) throws ExecutionException, InterruptedException {// 匿名类的方式构造一个Callable的实现类Callable callable = new Callable() {@Overridepublic Integer call() throws Exception {return 1;}};// 将Callable的实现类传入FutureTask task = new FutureTask<>(callable);// FutureTask就是Runnable的实现类,直接传到构造函数中即可Thread thread = new Thread(task);// 开启线程thread.start();// 阻塞拿到返回值int x = task.get();System.out.println(x);
}
接下来我们来分析一下 FutureTask 的源码,为接下来的线程池源码解析打好基础!
public class FutureTask implements RunnableFuture {// 表示当前task任务的状态 private volatile int state;// 新建状态,当前任务尚未执行 private static final int NEW = 0;// 当前任务正在结束,尚未完全结束,一种临界状态private static final int COMPLETING = 1;// 当前任务正常结束private static final int NORMAL = 2;// 当前任务执行过程中发生了异常,内部封装的callable.run() 向上抛出了异常private static final int EXCEPTIONAL = 3;// 当前任务被取消private static final int CANCELLED = 4;// 当前任务中断中private static final int INTERRUPTING = 5;// 当前任务已中断private static final int INTERRUPTED = 6; /** 线程池调用submit()方法时将Runnable变为Callable(适配器模式)*/private Callable callable;/** 正常情况下:任务执行结束,outcome保存执行结果(callable的返回值)* 非正常情况:callable向上抛出异常,outcome保存异常*/private Object outcome; // 当前任务被线程执行期间,保存当前执行任务的线程引用private volatile Thread runner;// 因为会有很多线程去get当前任务的结果,所以这里使用了一种Stack的数据结构来存储线程private volatile WaitNode waiters;// WaitNode节点static final class WaitNode {// 引用当前线程volatile Thread thread;// 下一个节点volatile WaitNode next;WaitNode() { thread = Thread.currentThread(); }}
传入一个 Callable 接口的实现类
public FutureTask(Callable callable) {if (callable == null)throw new NullPointerException();// 为callable属性赋值 这里的callable就是程序员自己实现的业务类this.callable = callable;// 设置当前任务状态为NEW(0)this.state = NEW; }
传入一个 Runnable 接口的实现类
public FutureTask(Runnable runnable, V result) {/** 使用适配器模式将runnable转换为了callable 外部线程 通过get获取当前任务执行结果时,结果可能为 null 也可能为 传进来的值* (submit的重载方法中有一个只传入Runnable的方法,而它的result就为null:RunnableFuture ftask = newTaskFor(task, null);)* 底层实际上就是创建了Callable接口的实现类,在call方法里调用了传的runnable的run方法,然后将我们传入的result原生不动的返回了* RunnableAdapter 通过将 Runnable 组合到自身内部,再实现 Callable 接口来完成从 Runnable 到 Callable 的适配*/// 为内部的callable属性赋值this.callable = Executors.callable(runnable, result);// 设置任务为初始状态this.state = NEW;}
// ||
// ||
// \/public static Callable callable(Runnable task, T result) {if (task == null)throw new NullPointerException();return new RunnableAdapter(task, result);}
// ||
// ||
// \/static final class RunnableAdapter implements Callable {final Runnable task;final T result;RunnableAdapter(Runnable task, T result) {this.task = task;this.result = result;}public T call() {// 直接运行了run方法task.run();// 直接将传来的result返回了 传什么返回什么return result;}}
总结
FutureTask 的构造器主要就是传入一个 Callable 或者 Runnable 的实现类,为内部的 callable 属性赋值,当我们传入的是 Runnable 时,会将我们的 Runnable 通过适配器模式进行包装,包装成一个 Callable,返回值我们自定义(返回值没有意义)。
大致流程解析
NEW 的状态,不处于 NEW 的状态直接返回不执行。CAS修改内部的runner的引用 的方式去执行这个任务,CAS 失败说明其他线程抢到了这个任务,当前线程也直接返回。CAS修改runner指向为当前线程 成功就会去执行任务。NORMAL(正常结束),set() 方法调用 finishCompletion() 方法将所有的读线程全部唤醒,最后将 FutureTask 内部的 callable 置为 null。 // submit(runnable/callable) -> newTaskFor(runnable) -> execute(task) -> pool// 任务执行入口public void run() {/* * 1.先判断state是否处于NEW状态,非NEW状态的任务线程都不会执行* 2.尝试使用CAS更新内部的runner(Thread)属性为当前线程,CAS失败表示有其他线程竞争这个任务 */if (state != NEW ||!UNSAFE.compareAndSwapObject(this, runnerOffset,null, Thread.currentThread()))return;/** 执行到这里表示当前任务一定是NEW状态 并且 当前线程也抢占任务成功*/try {// 内部的callable为c赋值 就是程序员自己封装逻辑的callable 或者 装饰后的runnableCallable c = callable;/** 条件1:c != null 防止空指针异常* 条件2:state == NEW 防止外部线程 cancel掉当前任务*/if (c != null && state == NEW) {// 保留结果的引用V result;// true 表示callable.run 代码块执行成功 未抛出异常// false 表示callable.run 代码块执行失败 抛出异常boolean ran;try {// 调用程序员自定义的callable或者适配之后的runnable拿到结果result = c.call();// c.call未抛出任何异常,ran会设置为true 代码块执行成功ran = true;} catch (Throwable ex) {// 说明程序员自己写的逻辑块有bug了 抛出异常 将结果置为nullresult = null;// ran置为falseran = false;// 后面讲// 非正常情况下 有异常 会执行这个set方法setException(ex);}// 运行成功if (ran)// 说明当前c.call正常执行结束了// set就是用CAS方式设置任务的状态并将结果设置到outcome中set(result);}} finally {// 将当前线程的引用runner置为nullrunner = null;// 关于中断..后面讲int s = state;if (s >= INTERRUPTING)handlePossibleCancellationInterrupt(s);}}
/** 使用CAS的方式设置任务状态并将结果写入到outcome中*/ protected void set(V v) {// 使用CAS方式设置当前任务状态为 COMPLETING(1)完成中..// 有没有可能失败呢?外部线程等不及了,直接在set执行CAS之前 将task取消了(很小概率事件)if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {// 结果赋值给outcomeoutcome = v;// 将结果赋值给outcome之后,马上会将任务状态修改为NORMAL(2)正常结束状态(非cAS,直接操作内存进行写的过程)UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state// 后面讲// 猜一猜?最起码得把get()在此阻塞的线程 唤醒..finishCompletion();}}
protected void setException(Throwable t) {/** 使用CAS方式设置当前任务状态为 COMPLETING(1)完成中..*/if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {// 这里的t引用的就是 callable 向上层抛出来的异常,赋值给outcomeoutcome = t;// 修改任务状态为EXCEPTIONAL(3),即出现异常的状态UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL);// 讲完get()再说finishCompletion();}}
大致流程
NEW 或者 COMPLETING(未完全完成),会进入 awaitDone() 方法进行挂起。否则说明任务已完成,调用 report() 方法,取出 run 方法完成时向 outcome 写入的结果或者异常信息进行返回,可能抛出异常。 /** 场景:多个线程等待当前任务执行完成后的结果*/public V get() throws InterruptedException, ExecutionException {int s = state;/** 条件成立:当前任务未执行、正在执行、正完成 * 调用get()的外部线程(非runner线程)会被阻塞在get()方法上*/if (s <= COMPLETING)// 返回当前任务的状态(可能当前线程已经在里面睡了一会了..),如果内部抛出中断异常,get()方法也会向上抛// awaitDone 实现阻塞的方法 FutureTask最核心的地方s = awaitDone(false, 0L);// 返回结果return report(s);}
整个方法是一个自旋
(下面的第一次第二次第三次的前提条件时任务状态没有完成并且不处于接近完成时)
(头插头出的队列)。就将当前线程对应的WaitNode节点出栈,然后抛出中断异常。state。 /** 返回值是任务的状态*/private int awaitDone(boolean timed, long nanos)throws InterruptedException {// 假设此次是不带超时的方法 拿到的时间为0final long deadline = timed ? System.nanoTime() + nanos : 0L;// 引用当前线程 封装成WaitNode对象WaitNode q = null;// 表示当前线程WaitNode对象有没有入队/压栈boolean queued = false;// 自旋for (;;) {/** 进入这个if 说明当前线程已经被阻塞后唤醒了,并且是被其他线程使用中断的方式唤醒的, * interrupted()返回true后会将Thread的中断标记重置为false(即下一次再调用此方法时就会返回false)*/if (Thread.interrupted()) {// 将Node节点出栈removeWaiter(q);// get方法抛出中断异常throw new InterruptedException();}/** 线程被阻塞(挂起)后,被其他线程使用unpark()正常唤醒,会正常自旋,走下面逻辑*/ // 读取当前任务的最新状态int s = state;// 条件成立:表示当前任务已经有结果了.. 可能是好 可能是坏if (s > COMPLETING) {// 条件成立:说明已经为当前线程创建过node了,此时需要将node.thread = nullif (q != null)q.thread = null; // help GCreturn s; // 返回当前状态}// 条件成立:说明任务接近完成状态,这里让当前线程释放CPU,进入下一次抢占CPU的状态else if (s == COMPLETING) // cannot time out yetThread.yield();/** 第一次自旋:当前线程还未创建WaitNode对象,此时根据当前线程封装一个WaitNode对象*/ else if (q == null)q = new WaitNode();/** 第二次自旋:WaitNode对象已经创建,但是node对象还未入队*/else if (!queued)// q.next = waiters 当前线程node节点 next指向原队列的头节点(waiters一直指向队列的头)// CAS方式设置waiters引用指向 当前线程node(队列头结点),成功的话 queued == true 否则,可能其它线程先你一步入队了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// 当前get()操作的线程就会被park掉(阻塞) 线程状态变为WAITING状态 相当于休眠了// 除非有其他线程将你唤醒 或者 将当前线程中断LockSupport.park(this);}}
private void removeWaiter(WaitNode node) {if (node != null) {// 将node内部的thread置为nullnode.thread = null; // 自旋retry:for (;;) {/** 遍历链表,将指定节点删除,可能有些节点因为中断没有出队,但是内部的thread被设置为null了(见上面的方法)* 也将这些节点一起出队*//** pred 当前节点的前一个节点* q 当前节点* s 当前节点的下一个节点* 循环条件为:当前节点不为空* 每次将q指向下一位*/for (WaitNode pred = null, q = waiters, s; q != null; q = s) {s = q.next; // 赋值if (q.thread != null)pred = q; // pred指向q 开始下一轮循环// 前置条件:当前节点为null// 条件成立:说明当前节点不是头结点else if (pred != null) {// 将前置节点的指针指向下一个节点 相当于删除当前节点pred.next = s;// 如果前置节点此时也为空 直接continue再次进行自旋if (pred.thread == null) // check for racecontinue retry;}// 说明当前节点是头结点 进行CAS替换操作 将waiters当前节点的下一个节点else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,q, s))continue retry;}break; // 全部处理完则退出自旋}}}
outcome 中的结果并返回 /** 处理 get 方法的返回结果*/private V report(int s) throws ExecutionException {/** 正常情况下,outcome保存的是callable运行结束的结果* 非正常情况下,outcome保存的是callable抛出的异常对象*/Object x = outcome;// 条件成立:当前任务状态正常结束if (s == NORMAL)// 直接返回callable运算结果return (V)x;// 任务被结束if (s >= CANCELLED)// 抛出取消异常throw new CancellationException();// 执行到这,说明callable接口实现中,是有bug的.. 抛出异常throw new ExecutionException((Throwable)x);}
/** 在 set 方法中调用该方法*/private void finishCompletion() {// q指向waiters链表的头结点for (WaitNode q; (q = waiters) != null;) {/** 使用CAS设置waiters为null,是因为怕外部线程使用cancel取消当前任务也会触发finishCompletion(小概率事件)*/if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {// 自旋for (;;) {// 获取当前node节点封装的threadThread t = q.thread;// 条件成立,说明当前线程不为nullif (t != null) {q.thread = null; // help GC// 唤醒当前节点对应的线程LockSupport.unpark(t); }// next 当前节点的下一个节点WaitNode next = q.next;// 说明当前的节点时最后一个节点if (next == null)break; // 说明都唤醒完成 直接退出自旋q.next = null; // help gcq = next; // 指向下一个节点}break; // 此时也说明全部处理完成 跳出最外层自旋}}// done接口 里面什么都没有实现 是一个可以进行扩展的方法done();// 将callable设置为null help GCcallable = null; // to reduce footprint}
/* * 如果通过线程池submit提交了任务之后,这个任务迟迟没有结束,这时在外部可以通过future句柄调用它的 cancel 方法,尝试中断/取消当前任务* @param:mayInterruptIfRunning* true->如果当前任务处于正在运行状态,会给线程发一个中断信号* false->直接改变当线程状态* @return:cancel成功则返回true 否则返回false*/public boolean cancel(boolean mayInterruptIfRunning) {/* * 条件1:state == NEW 成立,表示当前任务处于运行中 或者 处于线程池 任务队列中..* 条件2:UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED))* CAS操作 尝试修改线程状态为中断或者是取消(看传来的参数)* 条件成立:说明修改状态成功,可以去执行下面逻辑了,否则 返回false 表示cancel失败*/if (!(state == NEW &&UNSAFE.compareAndSwapInt(this, stateOffset, NEW,mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))return false;try { // true,将线程设置为中断状态if (mayInterruptIfRunning) {try {// 执行任务的线程 有可能现在是null// null的情况就是:当前任务还在队列中,还没有线程去获取它Thread t = runner;// 条件成立:说明当前线程runner正在执行taskif (t != null)/** 调用线程的中断方法,如果程序是响应中断的话,会走中断逻辑* 假设程序不是响应中断的,那么啥也不会发生*/t.interrupt();} finally { // final state// 中断完成后将状态改为中断完成UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);}}} finally {// 调用 finishCompletion() 完成线程 会唤醒所有get阻塞的线程finishCompletion();}return true;}
参考
- 视频参考
- b站_小刘讲源码公开课【面试必问】硬核手撕Java线程池FutureTask源码
- 文章参考
- shstart7_线程池源码解析1.FutureTask源码解析
- 兴趣使然的草帽路飞_线程池源码分析_01 FutureTask源码分析
- 肆华_FutureTask阅读理解