Il existe un moyen, mais vous ne l'aimerez pas. La méthode suivante transforme a Future<T>
en 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();
}
});
}
De toute évidence, le problème avec cette approche est que pour chaque avenir , un fil sera bloqué pour attendre le résultat du futur - contredisant l'idée d'avenir. Dans certains cas, il peut être possible de faire mieux. Cependant, en général, il n'y a pas de solution sans attendre activement le résultat de l' avenir .
CompletableFuture.supplyAsync(supplier, new SinglethreadExecutor())
ne bloquerait au moins pas les threads communs du pool.Si la bibliothèque que vous souhaitez utiliser propose également une méthode de style de rappel en plus du style Future, vous pouvez lui fournir un gestionnaire qui complète CompletableFuture sans aucun blocage de thread supplémentaire. Ainsi:
Sans le rappel, la seule autre façon que je vois résoudre ce problème est d'utiliser une boucle d'interrogation qui place toutes vos
Future.isDone()
vérifications sur un seul thread, puis d'appeler complete chaque fois qu'un Future est geable.la source
Si votre
Future
est le résultat d'un appel à uneExecutorService
méthode (par exemplesubmit()
), le plus simple serait d'utiliser leCompletableFuture.runAsync(Runnable, Executor)
méthode à la place.De
à
le
CompletableFuture
est alors créé "nativement".EDIT: Poursuite des commentaires de @SamMefford corrigés par @MartinAndersson, si vous voulez passer a
Callable
, vous devez appelersupplyAsync()
, en convertissant leCallable<T>
en aSupplier<T>
, par exemple avec:Parce que
T Callable.call() throws Exception;
lève une exception etT Supplier.get();
ne le fait pas, vous devez intercepter l'exception pour que les prototypes soient compatibles.la source
CompletableFuture<T> future = CompletableFuture.supplyAsync(myCallable, myExecutor);
supplyAsync
reçoit unSupplier
. Le code ne se compilera pas si vous essayez de transmettre un fichierCallable
.Callable<T>
en un fichierSupplier<T>
.J'ai publié un petit projet de futur qui essaie de faire mieux que la manière simple dans la réponse.
L'idée principale est d'utiliser le seul thread (et bien sûr avec pas seulement une boucle de rotation) pour vérifier tous les états Futures à l'intérieur, ce qui permet d'éviter de bloquer un thread d'un pool pour chaque transformation Future -> CompletableFuture.
Exemple d'utilisation:
la source
Suggestion:
http://www.thedevpiece.com/converting-old-java-future-to-completablefuture/
Mais, fondamentalement:
Et, le CompletablePromise:
Exemple:
la source
CompletablePromiseContext
non-statique et prendrais le paramètre pour l'intervalle de vérification (qui est réglé à 1 ms ici) puis surchargerais leCompletablePromise<V>
constructeur pour pouvoir fournir le vôtreCompletablePromiseContext
avec un intervalle de vérification éventuellement différent (plus long) pour les longues périodesFuture<V>
où vous ne Vous ne devez absolument pas pouvoir exécuter le rappel (ou composer) immédiatement après avoir terminé, et vous pouvez également avoir une instance deCompletablePromiseContext
pour regarder un ensemble deFuture
(au cas où vous en auriez beaucoup)Permettez-moi de suggérer une autre option (espérons-le, meilleure): https://github.com/vsilaev/java-async-await/tree/master/com.farata.lang.async.examples/src/main/java/com/farata /concurrent
En bref, l'idée est la suivante:
CompletableTask<V>
interface - l'union duCompletionStage<V>
+RunnableFuture<V>
ExecutorService
pour revenirCompletableTask
dessubmit(...)
méthodes (au lieu deFuture<V>
)L'implémentation utilise une autre implémentation CompletionStage (attention, CompletionStage plutôt que CompletableFuture):
Usage:
la source