Guava Futures.transform with "Real" Executor 与 transformAsync with DirectExecutor

Guava Futures.transform with "Real" Executor vs. transformAsync with DirectExecutor

假设我有两个重量级的 IO 阻塞操作,findMatchInSomeDB() 和 getDetailsFromOtherDB(String objectKey)。此外,我希望它们在后台 运行,并使用 Guava Futures 将它们链接在一起,因为一个取决于另一个 的结果(我知道这可以在一个简单的 Callable 中按顺序完成,但出于说明目的保持简单):

下面的 consumeChainconsumeChainAsync 方法之间是否存在任何实际或细微差别?

import com.google.common.base.Function;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;

public class Consumer
{
   private final Retriever retriever;
   private final ListeningExecutorService executorSvc;

   public Consumer(Retriever retriever, ListeningExecutorService executorSvc)
   {
      this.retriever = retriever;
      this.executorSvc = executorSvc;
   }

   private void consumeChain(String searchCriteria) throws Exception
   {
      ListenableFuture<String> futureMatch = executorSvc.submit(
         () -> retriever.findMatchInSomeDB(searchCriteria));

      Function<String, DataObject> keyToDataObj = objectKey ->
         retriever.getDetailsFromOtherDB(objectKey);

      // using "real" executor service so transform function runs
      // in the background
      ListenableFuture<DataObject> futureDataObj = Futures.transform(
         futureMatch, keyToDataObj, executorSvc);

      // ...do some other stuff in this thread...

      // do something with futureDataObj result
      futureDataObj.get();
   }

   private void consumeChainAsync(String searchCriteria) throws Exception
   {
      ListenableFuture<String> futureMatch = executorSvc.submit(
         () -> retriever.findMatchInSomeDB(searchCriteria));

      AsyncFunction<String, DataObject> keyToDataObj = objectKey ->
      {
         return executorSvc.submit(
            () -> retriever.getDetailsFromOtherDB(objectKey));
      };

      // using DirectExecutor because the transform function
      // only creates and submits a Callable
      ListenableFuture<DataObject> futureDataObj = Futures.transformAsync(
         futureMatch, keyToDataObj, MoreExecutors.directExecutor());

      // ...do some other stuff in this thread...

      // do something with futureDataObj
      futureDataObj.get();
   }
}

据我所知,两者都将通过 executorSvc 运行 每个重量级操作,并且都将传播取消 and/or 执行失败。

似乎 transformAsync 的唯一要点(而不是仅将 transform 与 DirectExecutor 以外的执行程序一起使用)是在您使用 API 时 returns ListenableFuture 而不是直接 运行ning 操作。我错过了什么吗?

It seems that the only point of transformAsync (instead of just using transform with an executor other than DirectExecutor) is for when you're working with an API that returns ListenableFuture instead of directly running the operation.

我想就是这样。

不过,我能想到一个让 transformAsync 稍微好一点的小区别:如果你在输出 Future 上调用 cancel(true)transform 当前 won't interrupt the thread that runs getDetailsFromOtherDB.相反,transformAsync 会(通过在 ListeningExecutorService 返回的 ListenableFuture 上调用 cancel(true))。 transform 应该 传播中断,但要做到这一点有一些微妙之处,在上面的 link 中有所介绍。