Java 8ではCompletableFuture
、構成可能なFutureの新しい実装であるが導入されています(thenXxxメソッドの束が含まれています)。これを排他的に使用したいのですが、使用したいライブラリの多くは、合成できないものだけを返しますFuture
インスタンスます。
返されたFuture
インスタンスをの中にラップして、CompleteableFuture
それを作成できるようにする方法はありますか?
回答:
方法はありますが、気に入らないでしょう。次のメソッドは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の結果を待つためにスレッドがブロックされることです。これは、futuresの概念と矛盾します。場合によっては、より良い結果が得られる可能性があります。ただし、一般的には、未来の結果を積極的に待たなければ解決策はありません。
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が取得可能になるたびにcompleteを呼び出すポーリングループを使用することです。
メソッドのFuture
呼び出しExecutorService
(などsubmit()
)の結果である場合は、CompletableFuture.runAsync(Runnable, Executor)
代わりにメソッドを使用するのが最も簡単です。
から
Runnbale myTask = ... ;
Future<?> future = myExecutor.submit(myTask);
に
Runnbale myTask = ... ;
CompletableFuture<?> future = CompletableFuture.runAsync(myTask, myExecutor);
CompletableFuture
その後、「ネイティブ」が作成されます。
EDIT:@SamMeffordでコメントを追求あなたが渡したい場合は、@MartinAnderssonによって修正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ミリ秒に設定されている)チェック間隔のためのparamを取る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);