Callable
Callable在包java.util.concurrent中,在java 1.5版本加入。
Callable与Runnable
Runnable
当线程执行Runnable时,都是通过其run()执行具体工作,执行完毕后线程结束。若想在执行完成,需要在相应的环节增加回调,或通过CountDownLatch等手段处理。
Callable
Callable的一般是配合ExecutorService使用的,而ExecutorService的常用实现一般为通过Executors的几个静态方法生成的ThreadPoolExecutor实例。
Executors.java
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
ThreadPoolExecutor、AbstractExecutorService、ExecutorService关系类图如下
ExecutorService只是接口,submit具体实现在AbstractExecutorService中,可见无论submit传入的是Callable还是Runnable最后都封装为FutureTask并给execute方法处理同时返回FutureTask。
AbstractExecutorService.java
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
FutureTask
FutureTask实现了RunnableFuture接口,RunnableFuture继承自Runnable和Future,所以FutureTask既可以作为Runnable直接运行,又包含了Future中的get、cancel及isXXX方法,可以获取线程状态并做处理。
从FutureTask的两个构造函数可知,传入的Runnable和泛型返回值,会构造成Callable后续流程与其一致。
FutureTask.java
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
FutureTask有两个关键方法get()和cancel(boolean mayInterruptIfRunning)
get()
注意:调用get方会阻塞直到任务执行结束返回结果为止,在Android主线程上调用会导致主线程block
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 V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
.....
long startTime = 0L; // Special value 0L means not yet parked
WaitNode q = null;
boolean queued = false;
for (;;) {
int s = state;
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING)
// We may have already promised (via isDone) that we are done
// so never return empty-handed or throw InterruptedException
Thread.yield();
else if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
.......
LockSupport.park(this);//阻塞当前线程
}
}
在get过程中,会自旋判断当前状态当状态值大于COMPLETING时返回(线程中的任务执行完成会将STATE从COMPLETING更新为NORMAL),或者有中断信号则会中断并抛出InterruptedException。返回值为Callable类中call方法的返回值。
cancel(boolean mayInterruptIfRunning)
public void run() {
//state不为NEW直接return(如state为CANCEL)
if (state != NEW ||
!RUNNER.compareAndSet(this, 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 {
.......
}
}
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW && STATE.compareAndSet
(this, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
STATE.setRelease(this, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}
参考run()的源码,FutureTask的state为NEW时有两种可能
1.新建未执行
2.执行中
根据cancel的参数boolean mayInterruptIfRunning的值不同分为两种情况
-
mayInterruptIfRunning is true
如果当前FutureTask的state为NEW,并且任务已经在执行中,则会将state设置为INTERRUPTING,并调用其所在的线程interrupt(),发送中断信号。完成后state置为INTERRUPTED
-
mayInterruptIfRunning is false
只是将state的值从NEW改为CANCELLED。后续当此FutureTask开始执行时,在其run()会进行判断,若state不为NEW则退出,此方式更符合整个Future、FutureTask结构的设计。
public void run() { if (state != NEW || !RUNNER.compareAndSet(this, null, Thread.currentThread())) return; .......
代码示例
FutureTask以及Future的特殊性主要体现在获取运行中状态和任务的cancel。
示例中创建5个任务并submit到单线程的ThreadPool中执行(模拟排队处理的情况)
private void cancelFuture(){
ExecutorService executor = Executors.newFixedThreadPool(1);
ArrayList<Future<Integer>> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Future<Integer> future = executor.submit(new CallableImpl(i));
list.add(future);
}
// 先让第一个任务执行10ms
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
// cancel第一个任务
list.get(0).cancel(false);
executor.shutdown();
}
static class CallableImpl implements Callable<Integer> {
int num;
CallableImpl(int num) {
this.num = num;
}
@Override
public Integer call() throws Exception {
int val = 0;
for (int i = 0; i < 10; i++) {
val++;
Thread.sleep(10);
}
System.out.println(String.format("Task %d complete", num));
return val;
}
}
在第0个任务执行过程中,调用其Future的cancel这里分几种情况说明
1.任务运行中,mayInterruptIfRunning为false
private void cancelFuture(){
...
// cancel第一个任务
list.get(0).cancel(false);
...
}
cancel无效,输出为:
Task 0 complete Task 1 complete Task 2 complete Task 3 complete Task 4 complete
2.任务运行中,mayInterruptIfRunning为true
private void cancelFuture(){
...
// cancel第一个任务
list.get(0).cancel(true);
...
}
第0个任务被cancel,输出为:
Task 1 complete Task 2 complete Task 3 complete Task 4 complete
第0个未执行完成
3.cancel多个任务,mayInterruptIfRunning为false
private void cancelFuture(){
....
// cancel多个任务
list.get(0).cancel(false);
list.get(1).cancel(false);
list.get(2).cancel(false);
....
}
当mayInterruptIfRunning为false,除第0个运行中的无法cancel,其他被cancel
输出为:
Task 0 complete Task 3 complete Task 4 complete
4.cancel多个任务,mayInterruptIfRunning为true
private void cancelFuture(){
...
// cancel第一个任务
list.get(0).cancel(true);
list.get(1).cancel(true);
list.get(2).cancel(true);
...
}
任务都被停止,输出为
Task 3 complete Task 4 complete
总结
Future接口的实现类为FutureTask,当需要获取task状态时可通过其相应的isDone、isCancelled获得。
FutureTask.cancel(boolean mayInterruptIfRunning),
当参数为false时,只是通过修改FutureTask的state为CANCELLED使得为开始的task取消执行,此种方式较为轻量影响较小。
当参数为true时,修改state为INTERRUPTING,同时向task所在线程发送interrupt()信号,与常规的线程中断相同,线程若可处理中断信号或处于WAITING、TIMED_WAITING都会被中断。此种方式较为生硬但可中断运行中的task.