根据 Guava 的例子,我看到了我一直在寻找解决问题的优雅方法。具体来说,我喜欢 Futures.addCallback(ListenableFuture, FutureCallback) 的工作方式,但是我希望能够对调用FutureCallback之前可以到期的时间长度设置超时。最佳的情况是,如果违反超时只是导致调用FutureCallback的失败条件。

Guava 已经有这样的东西了吗?是否只是不建议尝试将超时与回调耦合在一起?

编辑:包括导致我到这一点的代码示例。显然,我去除了有意义的部分以得到一个最小的例子。

@Test
public void testFuture()
{
    Callable<Boolean> callable = new Callable<Boolean>()
    {

        @Override
        public Boolean call() throws Exception
        {
            while(true);
        }
    };

    ListenableFuture<Boolean> callableFuture = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()).submit(callable);

    Futures.addCallback(callableFuture, new FutureCallback<Boolean>()
    {

        @Override
        public void onFailure(Throwable arg0)
        {
            System.out.println("onFailure:"+arg0);
        }

        @Override
        public void onSuccess(Boolean arg0)
        {
            System.out.println("onSuccess:"+arg0);
        }
    });

    try
    {
        callableFuture.get(1000, TimeUnit.MILLISECONDS);
    }catch(Throwable t)
    {
        System.out.println("catch:"+t);
    }
}

此代码将仅打印catch:java.util.concurrent.TimeoutException

最佳答案

更新:这已作为 Futures.withTimeout() 添加到 Guava 。

在内部,我们有一个makeTimeoutFuture方法,该方法将Future作为输入并返回一个新的Future,除非原始结果在给定的期限内未完成,否则该结果将具有相同的结果。如果截止日期到期,则输出Future的结果将设置为TimeoutException。因此,您可以调用makeTimeoutFuture并将侦听器附加到输出Future
makeTimeoutFuture不是您问题的最自然解决方案。实际上,我认为该方法主要是为了对no-arg get()调用设置硬超时而创建的,因为将所需的截止日期传播给所有调用者可能很痛苦。一个更自然的解决方案是,将get()视为get(long, TimeUnit),将addCallback(ListenableFuture, FutureCallback)归为addCallback(ListenableFuture, FutureCallback, long, TimeUnit, SchededuledExecutorService)。有点笨拙,尽管比makeTimeoutFuture少。在要做任何事情之前,我想再三考虑一下。你会file a feature request吗?

(这是我们内部拥有的:)

public static <V> ListenableFuture<V> makeTimeoutFuture(
    ListenableFuture<V> delegate,
    Duration duration,
    ScheduledExecutorService scheduledExecutor)

返回一个委托(delegate)给另一个人的 future ,但如果指定的持续时间到期,则将提前完成(通过包裹在TimeoutException中的ExecutionException)。在这种情况下不会取消委托(delegate)的将来。
scheduledExecutor.schedule(new Runnable() {
  @Override public void run() {
    TimeoutFuture.this.setException(new TimeoutException("Future timed out"));
  }
}, duration.getMillis(), TimeUnit.MILLISECONDS);

09-27 17:45