TL;DR:虽然可能有一些侵入性的方法(即反射),但ExecutionCompletionService.poll() 方法返回的Future 不会暴露已完成的Callable .
查看ExecutionCompletionService.submit(Callable) 的JDK 9 源代码,提交的Callable 包装在RunnableFuture 中(如果我们查看newTaskFor(Callable),其实际类型为FutureTask):
public Future<V> submit(Callable<V> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task);
executor.execute(new QueueingFuture<V>(f, completionQueue));
return f;
}
private RunnableFuture<V> newTaskFor(Callable<V> task) {
if (aes == null)
return new FutureTask<V>(task);
else
return aes.newTaskFor(task);
}
即使调用aes.newTaskFor(task),其中aes 是AbstractExecutorService,结果也是FutureTask:
public abstract class AbstractExecutorService implements ExecutorService {
// ...
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
}
如果我们查看QueueingFuture 内部类,我们会看到以下定义:
private static class QueueingFuture<V> extends FutureTask<Void> {
QueueingFuture(RunnableFuture<V> task,
BlockingQueue<Future<V>> completionQueue) {
super(task, null);
this.task = task;
this.completionQueue = completionQueue;
}
private final Future<V> task;
private final BlockingQueue<Future<V>> completionQueue;
protected void done() { completionQueue.add(task); }
}
在submit 方法中传递给新创建的QueueingFuture 的completionQueue 是一个BlockingQueue<Future<T>>,它存储与提供给submit 的已完成Callable 对象对应的Future 对象。换句话说,一旦使用submit 方法提交给ExecutorCompletionService 的Callable 执行完毕,对应于提交的Callable 的Future 将在completionQueue 中排队。 ExecutionCompletionService.poll() 方法只是委托给completionQueue 上的投票:
public Future<V> poll() {
return completionQueue.poll();
}
因此,我们可以获得对原始Callable 的引用的唯一方法是从ExecutionCompletionService.poll()(您的原始问题)返回的Future 对象中获取它。但是,查看Future 接口,不存在暴露任何Callable 的方法:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
即使FutureTask(从ExecutionCompletionService.poll()返回的实现类型)也不会暴露其内部Callable:
public class FutureTask<V> implements RunnableFuture<V> {
private Callable<V> callable;
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
// ...
}
}
如果FutureTask 有一些方法可以暴露底层Callable,那么我们也许可以对FutureTask 进行显式强制转换,但既然没有,那么该路由将无法解决手头的问题。
这里的问题是我们正在尝试将同步代码与异步ExecutionCompletionService 结合起来。以同步方式解决此问题的一种方法是在完成时返回 Callable 的 ID,以便在从 ExecutionCompletionService.poll() 返回的 Future 上调用 Future.get() 返回 ID(如您所述)。然后可以将 ID 与其原始Callable 配对。一种异步方法是使用原始Callable 注册一个回调,该回调在Callable 完成时调用。
希望解释比简单的否更有帮助。