Преобразование будущего 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);