Es gibt einen Weg, aber Sie werden es nicht mögen. Die folgende Methode wandelt a Future<T>
in a um 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();
}
});
}
Offensichtlich besteht das Problem bei diesem Ansatz darin, dass für jede Zukunft ein Thread blockiert wird, um auf das Ergebnis der Zukunft zu warten - ein Widerspruch zur Idee der Zukunft. In einigen Fällen ist es möglicherweise möglich, es besser zu machen. Im Allgemeinen gibt es jedoch keine Lösung, ohne aktiv auf das Ergebnis der Zukunft zu warten .
CompletableFuture.supplyAsync(supplier, new SinglethreadExecutor())
würde zumindest die allgemeinen Pool-Threads nicht blockieren.Wenn die Bibliothek, die Sie verwenden möchten, zusätzlich zum Future-Stil auch eine Rückrufmethode bietet, können Sie einen Handler bereitstellen, der die CompletableFuture ohne zusätzliche Thread-Blockierung abschließt. Wie so:
Ohne den Rückruf sehe ich die einzige andere Möglichkeit, dies zu lösen, darin, eine
Future.isDone()
Abfrageschleife zu verwenden, die alle Ihre Überprüfungen auf einen einzelnen Thread legt und dann vollständig aufruft, wenn eine Zukunft verfügbar ist.quelle
Ich habe ein kleines Zukunftsprojekt veröffentlicht , das versucht, die Antwort besser als den einfachen Weg zu machen .
Die Hauptidee besteht darin, den einzigen Thread (und natürlich nicht nur eine Spin-Schleife) zu verwenden, um alle darin enthaltenen Futures-Zustände zu überprüfen. Dadurch wird vermieden, dass ein Thread für jede Future -> CompletableFuture-Transformation aus einem Pool blockiert wird.
Anwendungsbeispiel:
quelle
Wenn Sie
Future
das Ergebnis eines Aufrufs einerExecutorService
Methode sind (z. B.submit()
), ist es am einfachstenCompletableFuture.runAsync(Runnable, Executor)
, stattdessen die Methode zu verwenden.Von
zu
Das
CompletableFuture
wird dann "nativ" erstellt.BEARBEITEN: Verfolgen von Kommentaren von @SamMefford, korrigiert von @MartinAndersson. Wenn Sie a übergeben möchten
Callable
, müssen Sie anrufensupplyAsync()
und dasCallable<T>
in ein konvertierenSupplier<T>
, z. B. mit:Da
T Callable.call() throws Exception;
eine Ausnahme ausgelöst wird undT Supplier.get();
dies nicht der Fall ist, müssen Sie die Ausnahme abfangen, damit Prototypen kompatibel sind.quelle
CompletableFuture<T> future = CompletableFuture.supplyAsync(myCallable, myExecutor);
supplyAsync
erhält eineSupplier
. Der Code wird nicht kompiliert, wenn Sie versuchen, a zu übergebenCallable
.Callable<T>
in a umzuwandelnSupplier<T>
.Vorschlag:
http://www.thedevpiece.com/converting-old-java-future-to-completablefuture/
Aber im Grunde:
Und das CompletablePromise:
Beispiel:
quelle
CompletablePromiseContext
nicht statisch machen und Parameter für das Prüfintervall (das hier auf 1 ms eingestellt ist) nehmen und dann denCompletablePromise<V>
Konstruktor überladen , um Ihrem eigenenCompletablePromiseContext
ein möglicherweise anderes (längeres) Prüfintervall für Langzeitläufe zur Verfügung zu stellen , inFuture<V>
denen Sie nicht arbeiten Sie müssen nicht unbedingt in der Lage sein, den Rückruf sofort nach Abschluss auszuführen (oder zu komponieren), und Sie können auch eine Instanz habenCompletablePromiseContext
, um eine Reihe vonFuture
(falls Sie viele haben)Lassen Sie mich eine andere (hoffentlich bessere) Option vorschlagen: https://github.com/vsilaev/java-async-await/tree/master/com.farata.lang.async.examples/src/main/java/com/farata /gleichzeitig
Kurz gesagt lautet die Idee wie folgt:
CompletableTask<V>
Schnittstelle einführen - die Vereinigung desCompletionStage<V>
+RunnableFuture<V>
ExecutorService
, umCompletableTask
vonsubmit(...)
Methoden zurückzukehren (anstelle vonFuture<V>
)Die Implementierung verwendet eine alternative CompletionStage-Implementierung (achten Sie darauf, CompletionStage anstelle von CompletableFuture):
Verwendung:
quelle