并发编程(三)Promise, Future 和 Callback

并发编程(三)Promise, Future 和 Callback

在并发编程中,我们通常会用到一组非阻塞的模型:Promise,Future 和 Callback。其中的 Future 表示一个可能还没有实际完成的异步任务的结果,针对这个结果可以添加 Callback 以便在任务执行成功或失败后做出对应的操作,而 Promise 交由任务执行者,任务执行者通过 Promise 可以标记任务完成或者失败。 可以说这一套模型是很多异步非阻塞架构的基础。

这一套经典的模型在 Scala、C# 中得到了原生的支持,但 JDK 中暂时还只有无 Callback 的 Future 出现,当然也并非在 Java 界就没有发展了,比如 Guava 就提供了 ListenableFuture 接口,而 Netty 4+ 更是提供了完整的 Promise、Future 和 Listener 机制。

一、Future 模式 - 将来式(JDK)

ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<Integer> future = executorService.submit(() -> {
    TimeUnit.SECONDS.sleep(5);
    return 5;
});
Integer result = future.get();

二、Future 模式--回调式(Guava)

Future 模式的第二种用法便是回调。很不幸的事,JDK 实现的 Future 并没有实现 callback, addListener 这样的方法,想要在 JAVA 中体验到 callback 的特性,得引入一些额外的框架。

<dependency>
   <groupId>com.google.guava</groupId>
   <artifactId>guava</artifactId>
   <version>21.0</version>
</dependency>
ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
ListenableFuture<Integer> future = service.submit(new Callable<Integer>() {
    public Integer call() throws Exception {
        TimeUnit.SECONDS.sleep(5);
        return 100;
    }
});
Futures.addCallback(future, new FutureCallback<Integer>() {
    public void onSuccess(Integer result) {
        System.out.println("success:" + result);
    }

    public void onFailure(Throwable throwable) {
        System.out.println("fail, e = " + throwable);
    }
});

Thread.currentThread().join();

三、Future 模式--回调式(Netty4)

Netty 除了是一个高性能的网络通信框架之外,还对 jdk 的Future 做了扩展,引入 Netty 的 maven 依赖

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.22.Final</version>
</dependency>
EventExecutorGroup group = new DefaultEventExecutorGroup(4); // 4 threads
io.netty.util.concurrent.Future<Integer> f = group.submit(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        TimeUnit.SECONDS.sleep(5);
        return 100;
    }
});
f.addListener(new FutureListener<Object>() {
    @Override
    public void operationComplete(io.netty.util.concurrent.Future<Object> objectFuture) throws Exception {
        System.out.println("计算结果::"+objectFuture.get());
    }
});

四、由 Callback Hell 引出 Promise 模式

同样的如果你对 ES6 有所接触,就不会对 Promise 这个模式感到陌生,如果你对前端不熟悉,也不要紧,我们先来看看回调地狱(Callback Hell)是个什么概念。

回调是一种我们推崇的异步调用方式,但也会遇到问题,也就是回调的嵌套。当需要多个异步回调一起书写时,就会出现下面的代码(以 js 为例):

asyncFunc1(opt, (...args1) => {
   asyncFunc2(opt, (...args2) => {
       asyncFunc3(opt, (...args3) => {
            asyncFunc4(opt, (...args4) => {
                // some operation
            });
        });
    });
});

虽然在 Java 业务代码中很少出现回调的多层嵌套,这样的代码不易读,嵌套太深修改也麻烦。于是 ES6 提出了 Promise 模式来解决回调地狱的问题。可能就会有人想问:Java 中存在 Promise 模式吗?答案是肯定的。

前面提到了 Netty 和 Guava 的扩展都提供了 addListener 这样的接口,用于处理 Callback 调用,但其实 jdk1.8 已经提供了一种更为高级的回调方式:CompletableFuture。首先尝试用 CompletableFuture 来解决回调的问题。

CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
    TimeUnit.SECONDS.sleep(5);
    return 100;
});
completableFuture.whenComplete((result, e) -> {
    System.out.println("结果:" + result);
});
Thread.currentThread().join();

五、Netty 中的 Promise 模式

Netty 文档说明 Netty 的网络操作都是异步的, 在源码上大量使用了 Future/Promise 模型,在 Netty 里面也是这样定义的:

  • Future 接口定义了 isSuccess(), isCancellable(), cause() 这些判断异步执行状态的方法。(read-only)
  • Promise 接口在 extends future 的基础上增加了 setSuccess(), setFailure() 这些方法。(writable)
public interface Future<V> {
    // 取消异步操作
    boolean cancel(boolean mayInterruptIfRunning);
    // 异步操作是否取消
    boolean isCancelled();
    // 异步操作是否完成,正常终止、异常、取消都是完成
    boolean isDone();
    // 阻塞直到取得异步操作结果
    V get() throws InterruptedException, ExecutionException;
    // 同上,但最长阻塞时间为timeout
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

Netty 对 JDK 的 Future 进行了扩展

public interface Future<V> extends java.util.concurrent.Future<V> {
    // 异步操作完成且正常终止
    boolean isSuccess();
    // 异步操作是否可以取消
    boolean isCancellable();
    // 异步操作失败的原因
    Throwable cause();
    // 添加一个监听者,异步操作完成时回调,类比javascript的回调函数
    Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener);
    Future<V> removeListener(GenericFutureListener<? extends Future<? super V>> listener);
    // 阻塞直到异步操作完成
    Future<V> await() throws InterruptedException;
    // 同上,但异步操作失败时抛出异常
    Future<V> sync() throws InterruptedException;
    // 非阻塞地返回异步结果,如果尚未完成返回null
    V getNow();
}

Netty 的 Promise 对又对 Future 进行了扩展

public interface Promise<V> extends Future<V> {    
    Promise<V> setSuccess(V result);
    boolean trySuccess(V result);
    Promise<V> setFailure(Throwable cause);
    boolean tryFailure(Throwable cause);
    boolean setUncancellable();
}

DefaultChannelPromise 是 ChannelPromise 的实现类,它是实际运行时的 Promoise 实例。

参考:

  1. 《并发编程 Promise, Future 和 Callback》:https://ifeve.com/promise-future-callback/

每天用心记录一点点。内容也许不重要,但习惯很重要!

猜你喜欢

转载自www.cnblogs.com/binarylei/p/10024266.html