2

假设您正在编写如下代码:

CompletionService<T> completion = new ExecutorCompletionService<>(new ExecutorService());

for(Callable callable : callableList) {
    completion.submit(callable);
}

// Do something else

while(true) {
    Future<T> future = completion.poll();

    // At this point, is there a way to find out which callable is returning this future?
}

如果您看到上面的代码,有没有办法找出在我调用completion.poll 时返回的可调用对象并有未来作为回报?

我可以扩展 Callable 以获得某种 ID 来做到这一点,但我想知道 java 本身是否有可能。

4

1 回答 1

0

TL;DR:虽然可能有一些侵入性的方式(即反射),但Future该方法返回的ExecutionCompletionService.poll()并不会暴露Callable已完成的内容。


查看 JDK 9 的源代码ExecutionCompletionService.submit(Callable),提交Callable的内容包含在 a 中(如果我们查看RunnableFuture,其实际类型是):FutureTasknewTaskFor(Callable)

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)被调用, whereaes是 a AbstractExecutorService,结果也是 a 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); }
}

completionQueue传递给方法中新创建QueueingFuture的是submita BlockingQueue<Future<T>>,它存储与提供给 的已完成对象Future相对应的对象。换句话说,一旦一个提交到使用该方法完成执行,对应于该提交的 将被排入. 该方法简单地委托给一个民意调查:CallablesubmitCallableExecutorCompletionServicesubmitFutureCallablecompletionQueueExecutionCompletionService.poll()completionQueue

public Future<V> poll() {
    return completionQueue.poll();
}

因此,我们可以获得对原始对象的引用的唯一方法是从(您的原始问题)返回的对象Callable中获取它。但是,查看接口,不存在公开任何方法的方法:FutureExecutionCompletionService.poll()FutureCallable

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,以便调用返回Future.get()FuturefromExecutionCompletionService.poll()返回 ID(如您所述)。然后可以将 ID 与其原始 ID 配对Callable。异步方法是使用完成Callable时调用的原始回调注册回调Callable

希望解释比简单的no更有帮助。

于 2018-04-03T21:50:02.507 回答