我有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()
必须为真。
答案 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"));
}
});