线程池的实现焦点之一是FutureTask。在提交任务时,用户实现的Callable实例task会被包装为FutureTask实例ftask;提交后任务异步执行,无需用户体贴;当用户需要时,再挪用FutureTask#get()获取功效——或异常。
随之而来的问题是,如何优雅的获取ftask的功效并处理惩罚异常?本文接头利用FutureTask的正确姿势。
本日换个气势气魄。
源码阐明
从提交一个Callable实例task开始。
submit()
ThreadPoolExecutor直接担任AbstractExecutorService的实现。
public abstract class AbstractExecutorService implements ExecutorService { ... public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask; } ... }
后续流程可参考源码|从串行线程关闭到工具池、线程池。最终会在ThreadPoolExecutor#runWorker()中执行task.run()。
task即5行建设的ftask,看newTaskFor()。
newTaskFor()
AbstractExecutorService#newTaskFor()建设一个RunnableFuture范例的FutureTask。
public abstract class AbstractExecutorService implements ExecutorService { ... protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { return new FutureTask<T>(callable); } ... }
看FutureTask的实现。
FutureTask
public class FutureTask<V> implements RunnableFuture<V> { ... 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; ... public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable } ... }
结构要领的重点是初始化ftask状态为NEW。
状态机
状态转换较量少,直接给状态序列:
* NEW -> COMPLETING -> NORMAL * NEW -> COMPLETING -> EXCEPTIONAL * NEW -> CANCELLED * NEW -> INTERRUPTING -> INTERRUPTED
状态在后头有用.
run()
简化如下:
public class FutureTask<V> implements RunnableFuture<V> { ... public void run() { 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 = null; int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } } ... }
假如执行时未抛出异常
假如未抛出异常,则ran==true,FutureTask#set()配置功效。
public class FutureTask<V> implements RunnableFuture<V> { ... protected void set(V v) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = v; UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state finishCompletion(); } } ... }
记着outcome。
相当于4行获取独有锁,5-6行执行锁中的操纵(留意,7行是不加锁的)。
假如执行时抛出了异常
假如运行时抛出了异常,则被12行catch捕捉,昆山软件开发,昆山软件开发,FutureTask#setException()配置功效;同时,ran==false,因此不执行FutureTask#set()。
public class FutureTask<V> implements RunnableFuture<V> { ... protected void setException(Throwable t) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = t; UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state finishCompletion(); } } ... }
假如没有抛出异常在,则outcome记录正常功效;假如抛出了异常,则outcome记录异常。
假如认为正常功效和异常都属于“任务的输出”,则利用沟通的变量outcome记录是公道的;同时,利用差异的竣事状态区分outcome中记录的内容。
RUN()小结
FutureTask将用户实现的task封装为ftask,利用状态机和outcome打点ftask的执行进程。这些进程对用户是不行见的,直到用户挪用get()要领。
顺道大白了Callable实例是如何执行的,为什么实现Callable#call()要领时可以将受检异常抛到外层(而Runable#run()要领例必需在要领内处理惩罚,不能抛出)。