先上用例
static void t1() throws Exception {
FutureTask task = new FutureTask(() -> "name");
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future> future = executorService.submit(task);
System.out.println(future.get());
System.out.println(task.get());
executorService.shutdown();
}
static void t2() throws Exception {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future
再看结果
null
name
wang
思考:
在学习Future
接口的时候,对Future
的介绍是保存异步计算的结果,并通过get
方法阻塞获取异步计算结果。但是在t1
方法中future
并没有获取到结果,于是我决定一探究竟。最好的方法当然是研究源码,话不多说,直接上源码。
- 先看
ExecutorService
的submit
方法
Future> submit(Runnable task);
Future submit(Callable task);
Future submit(Runnable task, T result);
该方法有三个重载,分别是传入一个runnable
对象;传入一个callable
对象;传入一个runnable
和泛型对象。这三个方法在AbstractExecutorService
类中实现了。
public Future> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public Future submit(Callable task) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
public Future submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
可以看到在这三个实现中都只做了两件事:
- 第一件事调用了
newTaskFor
方法创建了一个RunnableFuture
对象, - 第二件事执行
execute
方法
首先我们来关注一下newTaskFor
这个方法:
protected RunnableFuture newTaskFor(Callable callable) {
return new FutureTask(callable);
}
protected RunnableFuture newTaskFor(Runnable runnable, T value) {
return new FutureTask(runnable, value);
}
可以看到这个方法有两个重载,该方法只做了一件事——创建了一个FutureTask
对象。
让我们来回顾一下FutureTask
的构造函数:
public FutureTask(Callable callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
在第二个构造函数里面通过调用Executors
的静态方法callable
,将runnable
接口对象包装成了callable
接口对象。
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() {
task.run();
return result;
}
}
RunnableAdapter
类实现了Callable
接口,并实现了call
方法;在该方法中首先执行了runnable
对象,接着返回传入的泛型对象。
接着我们看下execute
方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
忘记了的话可以参考一下这篇文章
在addWorker
方法里面执行了线程,其实就是执行了FutureTask
对象的run
方法。
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable 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 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);
}
}
忘记了的话可以参考这篇文章
在run
方法里面有这一段代码result = c.call();
,这段代码执行了callable
对象的方法。接下来执行set(result);
把结果赋值给outcome
属性。
- 读完上面的代码我们可以解释为什么
t1
方法的future
获取不到值了
由于FutureTask
实现了Runnable
接口,所以执行的是Future> submit(Runnable task)
方法。
(1)该方法里面通过调用newTaskFor(task, null)
创建了RunnableFuture
对象。再来看下该对象的创建过程:
protected RunnableFuture newTaskFor(Runnable runnable, T value) {
return new FutureTask(runnable, value);
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
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() {
task.run();
return result;
}
}
可以看到FutureTask
的callable
属性是一个RunnableAdapter
对象,该RunnableAdapter
对象的task
属性就是我们调用executorService.submit(task);
传入的FutureTask
对象。result
属性是调用newTaskFor(task, null)
时传入的空值。
注意,此时我们已经有两个
FutureTask
对象了,一个是JDK帮我们创建对象,该FutureTask
对象的callable
属性是一个RunnableAdapter
对象;一个是我们自己创建的FutureTask task = new FutureTask(() -> "name");
对象,该FutureTask
对象的callable
属性是一个我们创建的lambda表达式。
(2) 该方法里面通过执行了execute
方法,execute
方法里面传入的参数是一个JDK帮我们创建的FutureTask
对象(为了区分,称它为f1)。在execute
方法里面执行了addWorker
方法;在addWorker
方法里面执行了线程,其实就是执行了FutureTask
对象的run
方法;在run
方法里面调用了call
方法。由于该callable
对象是一个RunnableAdapter
对象,所以会执行下面两行代码:
public T call() {
task.run();
return result;
}
由于该task
对象就是我们创建的FutureTask task = new FutureTask(() -> "name");
对象(为了区分,称它为f2),所以又会执行FutureTask
对象的run
方法;在run
方法里面调用了call
方法。由于这个callable
属性是一个我们创建的lambda表达式。所以会直接返回一个字符串,这个字符串会被赋值给f2的outcome
属性。当f2的run
方法执行完之后,RunnableAdapter
对象的call
方法会返回result
对象,即空值。该空值会被赋值给f1的outcome
属性。所以当调用future.get()
方法时会获取到空值,当调用task.get()
方法时会获取到字符串name
。
- 同时我们此时也应该明白为什么
t2
方法里面能够获取到值了
由于我们调用的是
方法。该方法通过Future submit(Callable task) newTaskFor(Callable
创建了一个callable) RunnableFuture
对象,看下创建过程:
protected RunnableFuture newTaskFor(Callable callable) {
return new FutureTask(callable);
}
public FutureTask(Callable callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
可以看到该FutureTask
对象的callable
属性就是我们声明的一个lambda表达式,该表达式返回的字符串最终被赋值给outcome
属性,所以但我们调用future.get()
方法时会获取到字符串wang
。