【问题标题】:Is there an easy way to turn Future<Future<T>> into Future<T>?有没有一种简单的方法可以将 Future<Future<T>> 变成 Future<T>?
【发布时间】:2010-01-29 21:07:48
【问题描述】:

我有一些代码可以将请求提交给另一个线程,该线程可能会也可能不会将该请求提交给另一个线程。这会产生Future&lt;Future&lt;T&gt;&gt; 的返回类型。是否有一些不令人发指的方法可以立即将其变成等待整个未来链完成的Future&lt;T&gt;

我已经在使用 Guava 库来处理其他有趣的并发东西,并作为 Google Collections 的替代品,它运行良好,但我似乎找不到适合这种情况的东西。

【问题讨论】:

  • 如果您可以添加更多上下文会很有帮助。显而易见的答案是调用 get(),但这可能不是您想要的。
  • 完成。抱歉,不清楚。
  • 听起来你需要 Monad

标签: java concurrency guava


【解决方案1】:

使用番石榴库的另一种可能的实现方式要简单得多。

import java.util.concurrent.*;
import com.google.common.util.concurrent.*;
import com.google.common.base.*;

public class FFutures {
  public <T> Future<T> flatten(Future<Future<T>> future) {
    return Futures.chain(Futures.makeListenable(future), new Function<Future<T>, ListenableFuture<T>>() {
      public ListenableFuture<T> apply(Future<T> f) {
        return Futures.makeListenable(f);
      }
    });
  }
}

【讨论】:

  • 看起来它可以做到,让我将所有 Future 交给番石榴。
【解决方案2】:

Guava 13.0 添加了Futures.dereference 来执行此操作。它需要ListenableFuture&lt;ListenableFuture&gt;,而不是普通的Future&lt;Future&gt;。 (在普通的 Future 上操作需要一个 makeListenable 调用,每个调用都需要一个用于任务生命周期的专用线程(方法的新名称JdkFutureAdapters.listenInPoolThread 更清楚地说明了这一点。)

【讨论】:

    【解决方案3】:

    我认为这是执行 Future 合约所能做的最好的事情。我采取了尽可能不聪明的策略,以确保它符合合同。尤其是 get with timeout 的实现。

    import java.util.concurrent.*;
    
    public class Futures {
      public <T> Future<T> flatten(Future<Future<T>> future) {
        return new FlattenedFuture<T>(future);
      }
    
      private static class FlattenedFuture<T> implements Future<T> {
        private final Future<Future<T>> future;
    
        public FlattenedFuture(Future<Future<T>> future) {
          this.future = future;
        }
    
        public boolean cancel(boolean mayInterruptIfRunning) {
          if (!future.isDone()) {
            return future.cancel(mayInterruptIfRunning);
          } else {
            while (true) {
              try {
                return future.get().cancel(mayInterruptIfRunning);
              } catch (CancellationException ce) {
                return true;
              } catch (ExecutionException ee) {
                return false;
              } catch (InterruptedException ie) {
                // pass
              }
            }
          }
        }
    
        public T get() throws InterruptedException, 
                              CancellationException, 
                              ExecutionException 
        {
          return future.get().get();
        }
    
        public T get(long timeout, TimeUnit unit) throws InterruptedException, 
                                                         CancellationException, 
                                                         ExecutionException, 
                                                         TimeoutException 
        {
          if (future.isDone()) {
            return future.get().get(timeout, unit);
          } else {
            return future.get(timeout, unit).get(0, TimeUnit.SECONDS);
          }
        }
    
        public boolean isCancelled() {
          while (true) {
            try {
              return future.isCancelled() || future.get().isCancelled();
            } catch (CancellationException ce) {
              return true;
            } catch (ExecutionException ee) {
              return false;
            } catch (InterruptedException ie) {
              // pass
            }
          }
        }
    
        public boolean isDone() {
          return future.isDone() && innerIsDone();
        }
    
        private boolean innerIsDone() {
          while (true) {
            try {
              return future.get().isDone();
            } catch (CancellationException ce) {
              return true;
            } catch (ExecutionException ee) {
              return true;
            } catch (InterruptedException ie) {
              // pass
            }
          }
        }
      }
    }
    

    【讨论】:

      【解决方案4】:

      你可以像这样创建一个类:

      public class UnwrapFuture<T> implements Future<T> {
          Future<Future<T>> wrappedFuture;
      
          public UnwrapFuture(Future<Future<T>> wrappedFuture) {
              this.wrappedFuture = wrappedFuture;
          }
      
          public boolean cancel(boolean mayInterruptIfRunning) {
              try {
                  return wrappedFuture.get().cancel(mayInterruptIfRunning);
              } catch (InterruptedException e) {
                  //todo: do something
              } catch (ExecutionException e) {
                  //todo: do something
              }
          }
          ...
      }
      

      您必须处理 get() 可以引发但其他方法不能引发的异常。

      【讨论】:

      • 这几乎是我试图避免的。此外,您在那里获得的取消方法将使取消等到链中的第一个未来完成。这绝对不是我要找的。​​span>
      • "把它变成等待整个未来链完成的 Future?" ......我认为在你掌握它之前你不能取消第二个未来。但是在第一个未来返回它之前你无法得到它。
      • 好收获。虽然第二个未来是由第一个未来创建的,但我相信你可以让自己进入取消第一个未来的状态,但无论如何它都会产生第二个未来,你不能取消它。我打赌你可以通过Futures.makeListenable-ing 第一个未来并添加一个监听器来解决这个问题,该侦听器在返回时立即取消链接的未来。然后问题就变成了对该案例的测试。
      【解决方案5】:

      这是我第一次尝试它,但我确信它有很多问题。我很乐意将其替换为 Futures.compress(f) 之类的内容。

      public class CompressedFuture<T> implements Future<T> {
          private final Future<Future<T>> delegate;
      
          public CompressedFuture(Future<Future<T>> delegate) {
              this.delegate = delegate;
          }
      
          @Override
          public boolean cancel(boolean mayInterruptIfRunning) {
              if (delegate.isDone()) {
                  return delegate.cancel(mayInterruptIfRunning);
              }
              try {
                  return delegate.get().cancel(mayInterruptIfRunning);
              } catch (InterruptedException e) {
                  throw new RuntimeException("Error fetching a finished future", e);
              } catch (ExecutionException e) {
                  throw new RuntimeException("Error fetching a finished future", e);
              }
          }
      
          @Override
          public T get() throws InterruptedException, ExecutionException {
              return delegate.get().get();
          }
      
          @Override
          public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
              long endTime = System.currentTimeMillis() + unit.toMillis(timeout);
              Future<T> next = delegate.get(timeout, unit);
              return next.get(endTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
          }
      
          @Override
          public boolean isCancelled() {
              if (!delegate.isDone()) {
                  return delegate.isCancelled();
              }
              try {
                  return delegate.get().isCancelled();
              } catch (InterruptedException e) {
                  throw new RuntimeException("Error fetching a finished future", e);
              } catch (ExecutionException e) {
                  throw new RuntimeException("Error fetching a finished future", e);
              }
          }
      
          @Override
          public boolean isDone() {
              if (!delegate.isDone()) {
                  return false;
              }
              try {
                  return delegate.get().isDone();
              } catch (InterruptedException e) {
                  throw new RuntimeException("Error fetching a finished future", e);
              } catch (ExecutionException e) {
                  throw new RuntimeException("Error fetching a finished future", e);
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-11-08
        • 1970-01-01
        • 2019-10-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多