如何使用与某个谓词匹配的结果完成任何给定的CompletableFutures时,如何使未来完成?

时间:2018-01-25 14:21:56

标签: java asynchronous completable-future

我有CompletableFuture<MyService>的列表:

List<CompletableFuture<MyService>>

其中MyService是不可变的,如下所示:

public class MyService {
    public boolean isAvailable() {
         return true; // or false
    }
}

我现在想要一个期货之一的未来:

  • 完成;
  • 针对该未来提供的MyService实例:MyService.isAvailable()返回true

继续时,我需要拥有可用的MyService实例。换句话说,我希望CompletableFuture<MyService>在满足两个条件时完成。

我该怎么做?我知道我可以使用CompletableFuture.anyOf(...)在其中一个期货完成时继续进行,但我不确定如何整合第二个要求:MyService.isAvailable()必须为真。

3 个答案:

答案 0 :(得分:2)

更新:我想我现在理解你的问题了。它可以帮助你代理这样的未来吗?

List<CompletableFuture<MyService>> givenFutures; // wherever they came from

CompletableFuture<MyService>[] myFutures = givenFutures.stream()
        .map(future -> {
            final CompletableFuture<MyService> futureWithCheck = new CompletableFuture<>();

            future.thenAccept(myService -> {
                if (myService.isAvailable()) {
                    futureWithCheck.complete(myService);
                }
            });

            return futureWithCheck;})
        .toArray(CompletableFuture[]::new);

// blocking
MyService availableService = (MyService) CompletableFuture.anyOf(myFutures).get();

// do what you want to do with the available service

更新2:我想到了关于thenCompose的问题,也许我的解决方案的中间部分可能表达如下:

CompletableFuture<MyService>[] myFutures = givenFutures.stream()
        .map(future ->
                future.thenCompose(myService ->
                        myService.isAvailable() ? CompletableFuture.completedFuture(myService) : new CompletableFuture<>()))
        .toArray(CompletableFuture[]::new);

旧答案,为了完整性:

(会发表评论,但声名太低)

看起来您要么必须重复轮询MyService.isAvailable()并在其返回true后再完成另一个CompletableFuture,否则{(1}}将会像Didier L所评论的那样返回它保留的Future跟踪并完成,一旦您提到的内部成员被更改。例如,所涉及的每个成员的设定者必须检查MyService是否为真,如果是,则完成之前发出的所有期货。

在这两种情况下,您都必须将附加isAvailable()与您拥有的Future链接起来。 Guavas Futures有一些有用的方法,但可能会CompletableFuture.allOf(…)完成这项工作。

答案 1 :(得分:1)

您可以尝试以下服务,只有在服务可用时才能完成未来:

public static class MyService {
    private String name;

    public MyService(String name) {
        this.name = name;
    }

    public String toString() {
        return name;
    }

    public CompletableFuture<MyService> isAvailable() {
        CompletableFuture<MyService> future = new CompletableFuture<>();
        Executors.newCachedThreadPool().submit(() -> {
            boolean available = checkAvailability();
            if (available)
                future.complete(this);
        });
        return future;
    }

    private boolean checkAvailability() {
        try {
            int ms = new Random().nextInt(1000);
            Thread.sleep(ms);
        } catch (InterruptedException e) {
        }
        return new Random().nextBoolean();
    }
}

然后使用这样的服务:

MyService s1 = new MyService("one");
MyService s2 = new MyService("two");
CompletableFuture<MyService> f1 = s1.isAvailable();
CompletableFuture<MyService> f2 = s2.isAvailable();

System.out.println("waiting for the first service to be available...");
CompletableFuture<Object> any = CompletableFuture.anyOf(f1, f2);
System.out.println("and the winner is: " + any.get());

// you can now safely cancel all futures
f1.cancel(true);
f2.cancel(true);

答案 2 :(得分:1)

考虑到CompletableFuture只能完成一次,您可以创建一个将在第一个未来完成的返回可用服务的文件:

List<CompletableFuture<MyService>> myServiceFutures = …

final CompletableFuture<MyService> availableMyServiceFuture = new CompletableFuture<>();
myServiceFutures.forEach(future -> future.thenAccept(myService -> {
    if (myService.isAvailable()) {
        // call will be ignored if already completed
        availableMyServiceFuture.complete(myService);
    }
}));

此外,如果没有可用的服务,您可能希望此未来能够异常完成:

CompletableFuture.allOf(myServiceFutures.toArray(new CompletableFuture[myServiceFutures.size()]))
        .whenComplete((e, r) -> {
            // TODO handle failure of individual futures if that can happen
            if (myServiceFutures.stream().map(CompletableFuture::join).noneMatch(MyService::isAvailable)) {
                availableMyServiceFuture.completeExceptionally(new IllegalStateException("No service is available"));
            }
        });