web-dev-qa-db-ja.com

Transform Java FutureからCompletableFutureへ

Java 8では、構成可能なFutureの新しい実装であるCompletableFutureが導入されています(thenXxxメソッドの束が含まれています)。これを排他的に使用したいのですが、使用したいライブラリの多くは、構成不可能なFutureインスタンスのみを返します。

返されるFutureインスタンスをCompleteableFuture内にラップして、構成できるようにする方法はありますか?

73
Dan Midwood

方法はありますが、気に入らないでしょう。次のメソッドは、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);
        }
    });
}

明らかに、このアプローチの問題は、各Futureに対して、スレッドがブロックされてFuture-futuresのアイデアと矛盾する結果を待つことです。場合によっては、もっとうまくやることができるかもしれません。ただし、一般的に、Futureの結果を積極的に待たなければ解決策はありません。

49
nosid

使用するライブラリが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がgettableになるたびにcompleteを呼び出すポーリングループを使用することです。

47
Kafkaesque

答えの中で 簡単な方法 より良くしようとする futureity プロジェクトを少し公開しました。

主なアイデアは、スレッドを1つだけ使用して(もちろん、スピンループだけでなく)内部のすべてのFutures状態をチェックすることです。

使用例:

Future oldFuture = ...;
CompletableFuture profit = Futurity.shift(oldFuture);
7

別の(できればもっと良い)オプションを提案させてください: https://github.com/vsilaev/Java-async-await/tree/master/com.farata.lang.async.examples/src/main/Java/com/farata/concurrent

簡単に言うと、アイデアは次のとおりです。

  1. _CompletableTask<V>_インターフェースの導入-_CompletionStage<V>_ + _RunnableFuture<V>_の結合
  2. ExecutorServiceをワープしてsubmit(...)メソッドからCompletableTaskを返します(_Future<V>_の代わりに)
  3. 完了、実行可能および構成可能な先物があります。

実装では、代替CompletionStage実装を使用します(注意を払ってください。CompletableFutureではなく、 CompletionStage )。

使用法:

_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); 
_
5
Valery Silaev

提案:

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);
        });
    }
}
5