如何处理连接多个异步CompletableFutures?

时间:2016-12-10 07:24:00

标签: java asynchronous completable-future

我已经读过CompletableFuture能够将多个期货与runAfterBoth合并,但如果我想合并多于两个呢?

CompletableFuture<Boolean> a = new CompletableFuture<>();
CompletableFuture<Boolean> b = new CompletableFuture<>();
CompletableFuture<Boolean> c = new CompletableFuture<>();

List<CompletableFuture<Boolean>> list = new LinkedList<>();

list.add(a);
list.add(b);
list.add(c);

// Could be any number
for (CompletableFuture<Boolean> f : list) {
   f.runAfter..
}

我的用例是我将消息发送到多个套接字以查找单个对象,这些对象可能在也可能不在其中任何一个上。

我目前正在将此视为一种解决方案:

CompletableFuture<Boolean> a = new CompletableFuture<>();
CompletableFuture<Boolean> b = new CompletableFuture<>();
CompletableFuture<Boolean> c = new CompletableFuture<>();

List<CompletableFuture<Boolean>> list = new LinkedList<>();

list.add(a);
list.add(b);
list.add(c);

CompletableFuture<Boolean> result = new CompletableFuture<>();

Thread accept = new Thread(() -> {
   for (CompletableFuture<Boolean> f : list)
      if (f.join() != null)
         result.complete(f.join());
});

accept.start();

// Actual boolean value returned
result.get();

但这有点乱。在我的情况下,我想在得到有效结果(非空)后继续处理,而不是等待无效结果。

例如,a需要5秒钟,即使b已在2秒内完成,循环也在等待它;但循环不知道,因为它仍在等待a

是否有一种模式可以加入多个异步期货,我可以在成功完成后立即回应?

另一种可能性:

public static class FutureUtil {
public static <T> CompletableFuture<T> anyOfNot(
   Collection<CompletableFuture<T>> collection,
   T value,
   T defaultValue)
{
   CompletableFuture<T> result = new CompletableFuture<>();

   new Thread(() -> {
      for (CompletableFuture<T> f : collection) {
         f.thenAccept((
            T r) -> {
            if ((r != null && !r.equals(value))
               || (value != null && !value.equals(r)))
               result.complete(r);
         });
      }

      try {
         for (CompletableFuture<T> f : collection)
            f.get();
      }
      catch (Exception ex) {
         result.completeExceptionally(ex);
      }

      result.complete(defaultValue);
   }).start();

   return result;
}
}

使用示例:

CompletableFuture<Boolean> a = new CompletableFuture<>();
CompletableFuture<Boolean> b = new CompletableFuture<>();
CompletableFuture<Boolean> c = new CompletableFuture<>();

List<CompletableFuture<Boolean>> list = new LinkedList<>();

list.add(a);
list.add(b);
list.add(c);

CompletableFuture<Boolean> result = FutureUtil.anyOfNot(list, null, false);

result.get();

1 个答案:

答案 0 :(得分:1)

如果您知道如果列表中的CF将使用非空值完成至少一个,您可以尝试:

public static <T> CompletableFuture<T> firstNonNull(List<CompletableFuture<T>> completableFutures) {

    final CompletableFuture<T> completableFutureResult = new CompletableFuture<>();
    completableFutures.forEach(cf -> cf.thenAccept(v -> {
        if (v != null) {
            completableFutureResult.complete(v);
        }
    }));
    return completableFutureResult;
}

如果无法保证至少有一个CF会返回非空值,则需要更复杂的内容:

public static <T> CompletableFuture<T> firstNonNull(List<CompletableFuture<T>> completableFutures, T defaultValue) {

    final CompletableFuture<T> completableFutureResult = new CompletableFuture<>();
    completableFutures.forEach(cf -> cf.thenAccept(v -> {
        if (v != null) {
            completableFutureResult.complete(v);
        }
    }));
    //handling the situation where all the CFs returned null 
    CompletableFuture<Void> allCompleted = CompletableFuture
        .allOf((CompletableFuture<?>[]) completableFutures.toArray());
    allCompleted.thenRun(() -> {
        //checking first if any of the completed delivered a non-null value, to avoid race conditions with the block above 
        completableFutures.forEach(cf -> {
            final T result = cf.join();
            if (result != null) {
                completableFutureResult.complete(result);
            }
        });
        //if still not completed, completing with default value
        if ( !completableFutureResult.isDone()) {
            completableFutureResult.complete(defaultValue);
        }
    });
    return completableFutureResult;
}