Java 8引入 CompletableFuture
了可组合的Future的新实现(包括一堆thenXxx方法)。我想专门使用此库,但是我要使用的许多库仅返回非组合库Future
实例。
有没有一种方法可以将返回的Future
实例包装到内,CompleteableFuture
以便我可以编写它?
Answers:
有一种方法,但是您不喜欢它。以下方法将a Future<T>
转换为a CompletableFuture<T>
:
public static <T> CompletableFuture<T> makeCompletableFuture(Future<T> future) {
if (future.isDone())
return transformDoneFuture(future);
return CompletableFuture.supplyAsync(() -> {
try {
if (!future.isDone())
awaitFutureIsDoneInForkJoinPool(future);
return future.get();
} catch (ExecutionException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
// Normally, this should never happen inside ForkJoinPool
Thread.currentThread().interrupt();
// Add the following statement if the future doesn't have side effects
// future.cancel(true);
throw new RuntimeException(e);
}
});
}
private static <T> CompletableFuture<T> transformDoneFuture(Future<T> future) {
CompletableFuture<T> cf = new CompletableFuture<>();
T result;
try {
result = future.get();
} catch (Throwable ex) {
cf.completeExceptionally(ex);
return cf;
}
cf.complete(result);
return cf;
}
private static void awaitFutureIsDoneInForkJoinPool(Future<?> future)
throws InterruptedException {
ForkJoinPool.managedBlock(new ForkJoinPool.ManagedBlocker() {
@Override public boolean block() throws InterruptedException {
try {
future.get();
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
return true;
}
@Override public boolean isReleasable() {
return future.isDone();
}
});
}
显然,这种方法的问题在于,对于每个Future,都会阻塞线程以等待Future的结果-与Future的想法相矛盾。在某些情况下,可能会做得更好。但是,总的来说,没有积极等待Future的结果就没有解决方案。
CompletableFuture.supplyAsync(supplier, new SinglethreadExecutor())
至少不会阻塞公共池线程。
如果您要使用的库除了提供Future样式之外还提供了回调样式方法,则可以为其提供一个处理程序,该处理程序可以完成CompletableFuture,而不会造成任何额外的线程阻塞。像这样:
AsynchronousFileChannel open = AsynchronousFileChannel.open(Paths.get("/some/file"));
// ...
CompletableFuture<ByteBuffer> completableFuture = new CompletableFuture<ByteBuffer>();
open.read(buffer, position, null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
completableFuture.complete(buffer);
}
@Override
public void failed(Throwable exc, Void attachment) {
completableFuture.completeExceptionally(exc);
}
});
completableFuture.thenApply(...)
没有回调,我认为解决此问题的另一种方法是使用轮询循环,该循环将所有Future.isDone()
检查置于单个线程上,然后在获取Future时调用完成。
如果您Future
是调用某个ExecutorService
方法(例如submit()
)的结果,那么最简单的CompletableFuture.runAsync(Runnable, Executor)
方法是改用该方法。
从
Runnbale myTask = ... ;
Future<?> future = myExecutor.submit(myTask);
至
Runnbale myTask = ... ;
CompletableFuture<?> future = CompletableFuture.runAsync(myTask, myExecutor);
的 CompletableFuture
则创建“原生地”。
编辑:通过@SamMefford进行@MartinAndersson纠正,如果要传递a Callable
,则需要调用supplyAsync()
,将转换Callable<T>
为Supplier<T>
,例如:
CompletableFuture.supplyAsync(() -> {
try { return myCallable.call(); }
catch (Exception ex) { throw new RuntimeException(ex); } // Or return default value
}, myExecutor);
因为T Callable.call() throws Exception;
会引发异常而T Supplier.get();
不会引发异常,所以您必须捕获异常,以便原型兼容。
CompletableFuture<T> future = CompletableFuture.supplyAsync(myCallable, myExecutor);
supplyAsync
收到Supplier
。如果您尝试传入,则代码将无法编译Callable
。
Callable<T>
为Supplier<T>
。
建议:
http://www.thedevpiece.com/converting-old-java-future-to-completablefuture/
但是,基本上:
public class CompletablePromiseContext {
private static final ScheduledExecutorService SERVICE = Executors.newSingleThreadScheduledExecutor();
public static void schedule(Runnable r) {
SERVICE.schedule(r, 1, TimeUnit.MILLISECONDS);
}
}
并且,CompletablePromise:
public class CompletablePromise<V> extends CompletableFuture<V> {
private Future<V> future;
public CompletablePromise(Future<V> future) {
this.future = future;
CompletablePromiseContext.schedule(this::tryToComplete);
}
private void tryToComplete() {
if (future.isDone()) {
try {
complete(future.get());
} catch (InterruptedException e) {
completeExceptionally(e);
} catch (ExecutionException e) {
completeExceptionally(e.getCause());
}
return;
}
if (future.isCancelled()) {
cancel(true);
return;
}
CompletablePromiseContext.schedule(this::tryToComplete);
}
}
例:
public class Main {
public static void main(String[] args) {
final ExecutorService service = Executors.newSingleThreadExecutor();
final Future<String> stringFuture = service.submit(() -> "success");
final CompletableFuture<String> completableFuture = new CompletablePromise<>(stringFuture);
completableFuture.whenComplete((result, failure) -> {
System.out.println(result);
});
}
}
CompletablePromiseContext
非静态对象并为检查间隔(此处设置为1 ms)获取参数,然后重载CompletablePromise<V>
构造函数,以便能够为您自己的设备CompletablePromiseContext
提供一个可能不同的(更长)检查间隔,以Future<V>
在您不运行的情况下长时间运行不必绝对能够在完成后立即运行回调(或编写),并且您还可以拥有一个实例CompletablePromiseContext
来观看一组Future
(如果有的话)
让我提出另一个(希望更好)的选择:https : //github.com/vsilaev/java-async-await/tree/master/com.farata.lang.async.examples/src/main/java/com/farata /同时
简而言之,想法如下:
CompletableTask<V>
界面- CompletionStage<V>
+ 的并集
RunnableFuture<V>
ExecutorService
返回的扭曲(而不是CompletableTask
submit(...)
Future<V>
)实现使用替代的CompletionStage实现(请注意,CompletionStage而不是CompletableFuture):
用法:
J8ExecutorService exec = J8Executors.newCachedThreadPool();
CompletionStage<String> = exec
.submit( someCallableA )
.thenCombineAsync( exec.submit(someCallableB), (a, b) -> a + " " + b)
.thenCombine( exec.submit(someCallableC), (ab, b) -> ab + " " + c);