Преобразование будущего Java в завершаемое будущее
В Java 8 представлена CompletableFuture
, новая реализация Будущего, которая является составной (включает в себя кучу методов thenXxx). Я бы хотел использовать это исключительно, но многие из библиотек, которые я хочу использовать, возвращают только неконсолируемые экземпляры Future
.
Есть ли способ обернуть возвращаемые экземпляры Future
внутри CompleteableFuture
, чтобы я мог его создать?
Ответы
Ответ 1
Есть способ, но вам это не понравится. Следующий метод преобразует a Future<T>
в CompletableFuture<T>
:
public static <T> CompletableFuture<T> makeCompletableFuture(Future<T> future) {
return CompletableFuture.supplyAsync(() -> {
try {
return future.get();
} catch (InterruptedException|ExecutionException e) {
throw new RuntimeException(e);
}
});
}
Очевидно, что проблема с этим подходом заключается в том, что для каждого Будущего поток будет заблокирован, чтобы ждать результата Будущего, что противоречит идее фьючерсов. В некоторых случаях это может быть лучше. Однако, в общем, нет решения без активного ожидания результата Будущего.
Ответ 2
Если библиотека, которую вы хотите использовать, также предлагает метод стиля обратного вызова в дополнение к стилю 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()
в один поток, а затем вызывает завершение всякий раз, когда будет получено значение "Будущее".
Ответ 3
Я опубликовал небольшой проект futurity, который пытается сделать лучше прямолинейный путь в ответе.
Основная идея состоит в том, чтобы использовать только один поток (и, конечно, не только цикл вращения), чтобы проверять все состояния Futures внутри, что помогает избежать блокировки потока из пула для каждого преобразования Future- > CompletableFuture.
Пример использования:
Future oldFuture = ...;
CompletableFuture profit = Futurity.shift(oldFuture);
Ответ 4
Позвольте мне предложить другой вариант (надеюсь, лучше):
https://github.com/vsilaev/java-async-await/tree/master/com.farata.lang.async.examples/src/main/java/com/farata/concurrent
Вкратце, идея такова:
- Ввести интерфейс
CompletableTask<V>
- объединение
CompletionStage<V>
+ RunnableFuture<V>
- Warp
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);