简体   繁体   English

将 Java Future 转换为 CompletableFuture

[英]Transform Java Future into a CompletableFuture

Java 8 introduces CompletableFuture , a new implementation of Future that is composable (includes a bunch of thenXxx methods). Java 8 引入了CompletableFuture ,一个可组合的 Future 的新实现(包括一堆 thenXxx 方法)。 I'd like to use this exclusively, but many of the libraries I want to use return only non-composable Future instances.我想专门使用它,但我想使用的许多库只返回不可组合的Future实例。

Is there a way to wrap up a returned Future instances inside of a CompleteableFuture so that I can compose it?有没有办法将返回的Future实例包装在CompleteableFuture中,以便我可以组合它?

If the library you want to use also offers a callback style method in addition to the Future style, you can provide it a handler that completes the CompletableFuture without any extra thread blocking.如果你想使用的库除了 Future 风格之外还提供了一个回调风格的方法,你可以为它提供一个处理程序来完成 CompletableFuture 而没有任何额外的线程阻塞。 Like so:像这样:

    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(...)

Without the callback the only other way I see solving this is to use a polling loop that puts all your Future.isDone() checks on a single thread and then invoking complete whenever a Future is gettable.如果没有回调,我认为解决此问题的唯一另一种方法是使用轮询循环,将所有Future.isDone()检查放在单个线程上,然后在 Future 可获取时调用 complete。

There is a way, but you won't like it.有一种方法,但你不会喜欢它。 The following method transforms a Future<T> into a CompletableFuture<T> :以下方法将Future<T>转换为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();
    }
  });
}

Obviously, the problem with this approach is, that for each Future , a thread will be blocked to wait for the result of the Future --contradicting the idea of futures.显然,这种方法的问题在于,对于每个Future ,都会阻塞一个线程以等待Future的结果——这与 Futures 的想法相矛盾。 In some cases, it might be possible to do better.在某些情况下,可能会做得更好。 However, in general, there is no solution without actively wait for the result of the Future .但是,一般情况下,不积极等待Future的结果是没有解决方案的。

If your Future is the result of a call to an ExecutorService method (eg submit() ), the easiest would be to use the CompletableFuture.runAsync(Runnable, Executor) method instead.如果您的Future是调用ExecutorService方法(例如submit() )的结果,最简单的方法是改用CompletableFuture.runAsync(Runnable, Executor)方法。

From

Runnbale myTask = ... ;
Future<?> future = myExecutor.submit(myTask);

to

Runnbale myTask = ... ;
CompletableFuture<?> future = CompletableFuture.runAsync(myTask, myExecutor);

The CompletableFuture is then created "natively". CompletableFuture然后是“本地”创建的。

EDIT: Pursuing comments by @SamMefford corrected by @MartinAndersson, if you want to pass a Callable , you need to call supplyAsync() , converting the Callable<T> into a Supplier<T> , eg with:编辑:通过@MartinAndersson 更正@SamMefford 的评论,如果您想传递Callable ,则需要调用supplyAsync() ,将Callable<T>转换为Supplier<T> ,例如:

CompletableFuture.supplyAsync(() -> {
    try { return myCallable.call(); }
    catch (Exception ex) { throw new CompletionException(ex); } // Or return default value
}, myExecutor);

Because T Callable.call() throws Exception;因为T Callable.call() throws Exception; throws an exception and T Supplier.get();抛出异常和T Supplier.get(); doesn't, you have to catch the exception so prototypes are compatible.没有,您必须捕获异常以便原型兼容。

A note on exception handling关于异常处理的说明

The get() method doesn't specify a throws , which means it should not throw a checked exception. get()方法未指定throws ,这意味着它不应抛出已检查的异常。 However, unchecked exception can be used.但是,可以使用未经检查的异常。 The code in CompletableFuture shows that CompletionException is used and is unchecked (ie is a RuntimeException ), hence the catch/throw wrapping any exception into a CompletionException . CompletableFuture中的代码显示使用了CompletionException并且未检查(即是RuntimeException ),因此 catch/throw 将任何异常包装到CompletionException

Also, as @WeGa indicated, you can use the handle() method to deal with exceptions potentially being thrown by the result:此外,正如@WeGa 所指出的,您可以使用handle()方法来处理结果可能抛出的异常:

CompletableFuture<T> future = CompletableFuture.supplyAsync(...);
future.handle((ex,res) -> {
        if (ex == null) {
            // An exception occurred ...
        } else {
            // No exception was thrown, 'res' is valid and can be handled here
        }
    });

I published a little futurity project that tries to make better than the straightforward way in the answer.我发布了一个小未来项目,试图在答案中做得比直接方式更好。

The main idea is to use the only one thread (and of course with not just a spin loop) to check all Futures states inside, which helps to avoid blocking a thread from a pool for each Future -> CompletableFuture transformation.主要思想是使用唯一的一个线程(当然不仅仅是一个自旋循环)来检查内部的所有 Futures 状态,这有助于避免为每个 Future -> CompletableFuture 转换阻塞池中的线程。

Usage example:用法示例:

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

Suggestion:建议:

http://www.thedevpiece.com/converting-old-java-future-to-completablefuture/ http://www.thedevpiece.com/converting-old-java-future-to-completablefuture/

But, basically:但是,基本上:

public class CompletablePromiseContext {
    private static final ScheduledExecutorService SERVICE = Executors.newSingleThreadScheduledExecutor();

    public static void schedule(Runnable r) {
        SERVICE.schedule(r, 1, TimeUnit.MILLISECONDS);
    }
}

And, the CompletablePromise:并且,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);
    }
}

Example:例子:

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

Let me suggest another (hopefully, better) option: https://github.com/vsilaev/java-async-await/tree/master/com.farata.lang.async.examples/src/main/java/com/farata/concurrent让我建议另一个(希望更好)选项: https : //github.com/vsilaev/java-async-await/tree/master/com.farata.lang.async.examples/src/main/java/com/farata /同时

Briefly, the idea is the following:简而言之,想法如下:

  1. Introduce CompletableTask<V> interface -- the union of the CompletionStage<V> + RunnableFuture<V>引入CompletableTask<V>接口CompletableTask<V> CompletionStage<V> + RunnableFuture<V>
  2. Warp ExecutorService to return CompletableTask from submit(...) methods (instead of Future<V> ) Warp ExecutorServicesubmit(...)方法(而不是Future<V> )返回CompletableTask
  3. Done, we have runnable AND composable Futures.完成了,我们有了可运行和可组合的 Futures。

Implementation uses an alternative CompletionStage implementation (pay attention, CompletionStage rather than CompletableFuture):实现使用替代的 CompletionStage 实现(注意, CompletionStage而不是 CompletableFuture):

Usage:用法:

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); 
public static <T> CompletableFuture<T> fromFuture(Future<T> f) {
    return CompletableFuture.completedFuture(null).thenCompose(avoid -> {
        try {
            return CompletableFuture.completedFuture(f.get());
        } catch (InterruptedException e) {
            return CompletableFuture.failedFuture(e);
        } catch (ExecutionException e) {
            return CompletableFuture.failedFuture(e.getCause());
        }
    });
}

The main idea goes like this:主要思想是这样的:

Future<?> future = null;
return CompletableFuture.supplyAsync(future::get);

However, you will receive some warnings from your compiler.但是,您会收到来自编译器的一些警告。

So, here is the first option.所以,这里是第一个选项。

Future<?> future = null;
return CompletableFuture.supplyAsync(
        ()->{
            try {
                return future.get();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });

Second Option, hide the try...catch via casting the functional interface.第二个选项,通过转换功能接口隐藏 try...catch。

    @FunctionalInterface
    public interface MySupplier<T> extends Supplier<T> {
        @Override
        default T get() {
            try {
                return getInternal();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        T getInternal() throws Exception;
    }


    public static void main(String[] args) {
        Future<?> future = null;
        return CompletableFuture.supplyAsync((MySupplier<?>) future::get);

    }


Third Option, find out some 3rd party lib which has provided such a functional interface.第三个选项,找出一些提供了这样一个功能接口的第三方库。

See Also: Java 8 Lambda function that throws exception?另请参阅: Java 8 Lambda function 抛出异常?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM