其他
我靠(call) ,我的未来(Future)在哪里???
The following article is from Java建设者 Author cxuan
继承 Thread 类; 实现 Runnable 接口; 实现 Callable 接口。
call()
方法可以异步地返回一个计算结果 Future,并且一般需要配合 ExecutorService 来执行。这一套操作在代码实现上似乎也并不难,可是对于call()方法具体怎么(被ExecutorService)执行的,以及 Future 这个结果是怎么获取的,却又不是很清楚了。承载着具体任务的 call() 方法如何被执行的? 任务的执行结果如何得到?
任务
、执行
、以及结果
这三个概念在 Java API 中的实体和各自的继承关系,第二部分通过一个简单的例子回顾他们的用法,再理解下这两个问题的答案。Callable、Executor 与 Future
任务:Callable
return null;
就可以了。对比的是 Runnable,另一个明显的区别则是 Callable可以抛出异常。public interface Executor {
void execute(Runnable command);
}
<T> Future<T> submit(Callable<T> task);
为例,其中 task 为用户定义的执行的异步任务,Future 表示了任务的执行结果,泛型 T 代表任务结果的类型。// 1.第一个重载方法,参数为Callable
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
// return new FutureTask<T>(callable);
execute(ftask);
return ftask;
}
// 2.第二个重载方法,参数为Runnable
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
// return new FutureTask<T>(task, null);
execute(ftask);
return ftask;
}
// 3.第三个重载方法,参数为Runnable + 返回对象
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
// return new FutureTask<T>(task, result);
execute(ftask);
return ftask;
}
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result; // 返回的结果;显然:需要在run()中赋值
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
结果:Future
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation unless it has been cancelled.
*/
void run();
}
public class FutureTask<V> implements RunnableFuture<V> {
private Callable<V> callable; // target,待执行的任务
/** 保存执行结果或异常,在get()方法中返回/抛出 */
private Object outcome; // 非volatile,通过CAS保证线程安全
public void run() {
......
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call(); // 调用call()执行用户任务并获取结果
ran = true; // 执行完成,ran置为true
} catch (Throwable ex) { // 调用call()出现异常,而run()方法继续执行
result = null;
ran = false;
setException(ex);
// setException(Throwable t): compareAndSwapInt(NEW, COMPLETING); outcome = t;
}
if (ran)
set(result);
// set(V v): compareAndSwapInt(NEW, COMPLETING); outcome = v;
}
}
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L); // 加入队列等待COMPLETING完成,可响应超时、中断
return report(s);
}
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
// 超时等待
}
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL) // 将outcome作为执行结果返回
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x); // 将outcome作为捕获的返回
}
}
第一,作为 Runnable 传入 execute() 方法来执行,同时封装 Callable 对象并在 run() 中调用其 call() 方法; 第二,作为 Future 管理任务的执行状态,将 call() 的返回值保存在 outcome 中以通过 get() 获取。这似乎就能回答开头的两个问题,并且浑然天成,就好像是一个问题,除非发生异常的时候返回的不是任务的结果而是异常对象。
二、使用举例
ExecutorService executor = Executors.newCachedThreadPool();
MyTask task = new MyTask();
使用Future
主线程启动线程池
子线程开始计算...
子线程计算中,用时 1 秒
子线程计算中,用时 2 秒
子线程计算中,用时 3 秒
子线程计算完成,返回:100
主线程得到返回结果:100
使用FutureTask
submit()
重载方法。FutureTask<Integer> futureTask = new FutureTask<>(task);
executor.submit(futureTask); // 作为Ruunable传入submit()中
System.out.println("主线程得到返回结果:"+futureTask.get()); // 作为Future获取结果
executor.shutdown();
submit(Callable<T> task)
而后者调用了submit(Runnable task)
,但最终都通过execute(futuretask)
来把任务加入线程池中。总结
线程池中,submit() 方法实际上将 Callable 封装在 FutureTask 中,将其作为 Runnable 的子类传给 execute()真正执行; FutureTask 在 run() 中调用 Callable 对象的 call() 方法并接收返回值或捕获异常保存在 Object outcome
中,同时管理执行过程中的状态state
;FutureTask 同时作为 Future 的子类,通过 get() 返回任务的执行结果,若未执行完成则通过等待队列进行阻塞等待完成;
2021-03-28
2021-03-27
2021-03-26