netty5.0之Future和Promise

9 篇文章 0 订阅

来自《Netty权威指南》 李林锋 著。

1、Future功能

Future最早来源于JDK的java.util.concurent.Future,它用于代表异步操作的结果。相关API如下:

可以通过get方法获取操作结果,如果操作尚未完成,则会同步阻塞当前调用的线程;如果不允许阻塞太长时间或者无限期阻塞,可以通过带超时时间的get方法获取结果;如果到达超时时间操作仍然没有完成,则跑出TimeoutException.

isDone()方法可以判断当前的异步操作是否完成,如果完成,无论成功与否,否会返回true,否则返回false。

通过cancel可以尝试取消异步操作,它的结果是未知的,如果操作已经完成,或者发生其他未知的原因拒绝取消,取消操作将会失败。

2、ChannelFuture功能介绍

由于Netty的Future都是与异步I/O操作相关的,因此,命名为ChannelFuture,代表它与Channel操作相关

在netty中,所有的I/O操作都是异步的,这意味着任何I/O调用都会立即返回,而不是像传统BIO那样等待操作完成。异步操作会带来一个问题:调用者如何获取异步操作的结果?ChannelFuture就是为了 解决这个问题二专门设计的。下面我们一起看它的原理。

ChannelFuture有两种状态:uncompleted和completed。当开始一个I/O操作时,一个新的ChannelFuture被创建,此时它处于uncompleted状态-非失败、非成功、非取消,因为I/O操作此时还没有完成。一旦I/O操作完成,ChannelFuture将会被设置成completed,它的结果有如下三种可能:操作成功、操作失败、操作被取消。

ChannelFuture提供了一系列新的API,用于获取操作结果(sync)、添加事件监听器(addListener),取消I/O操作,同步等待等。

Netty强烈建议直接通过添加监听器的方式获取I/O操作结果,或者进行后续的相关操作。

ChannelFuture可以同时增加一个或者多个GenericFutureListener,也可以通过remove方法删除GenericFutureListener。

GenericFutrueListener的接口定义如下:

public interface GenericFutureListener<F extends Future<?>> extends EventListener {

    /**
     * Invoked when the operation associated with the {@link Future} has been completed.
     *
     * @param future  the source {@link Future} which called this callback
     */
    void operationComplete(F future) throws Exception;
}
当I/O操作完成之后,I/O线程会回调ChannelFuture中GenericFutureListener的operationComplete方法,并把ChannelFuture对象当做方法的入参。如果用户需要做上下文相关的操作,需要将信息保存到对应的ChannelFuture中。

推荐通过GenericFutureListener代替ChannelFuture的get等方法的原因是:当我们进行异步I/O操作时,完成的时间是无法预测的,如果不设置超时时间,它会导致调用线程长时间被阻塞,甚至挂死。而设置超时时间,时间又无法精确预测。利用异步通知机制回调GenericFutureListener是最佳的解决方案,它的性能最优。

需要注意的是:不要在ChannelHandler中调用ChannelFuture的await()方法,这会导致死锁。原因是发起I/O操作后,由I/O线程负责异步通知发起I/O操作的用户线程,如果I/O线程和用户线程是同一个线程,就会导致I/O线程等待自己操作操作完成,这就导致了死锁,这跟经典的两个线程互等待死锁不同,属于自己把自己挂死。

3、Promise功能介绍

Promise是可写的Future,Future自身并没有写操作相关的接口,Netty通过Promise对Future进行扩展,用于设置I/O操作的结果。Future相关的接口定义如下:


Promise相关的写操作接口定义如下:

4、Promise源码分析

1)Promise继承关系图

由于I/O操作种类非常多,因此对于的Promise子类也非常多,它的继承关系如下图:

2)分析DefaultPromise

尽管Promise的子类种类繁多,但是它的功能相对比较清晰,代码页较为简单,因此我们只分析一个它的实现子类的源码,如果读者对其他子类感兴趣,可以自行学习。

setSuccess()方法对其操作结果进行判断,如果操作成功,则调用notifyListeners方法通知listener。而setSuccess0方法如下:首先判断当前Promise的操作结果是否已经被设置,如果已经被设置,则不允许重复设置,返回设置失败。

由于可能存在I/O线程和用户线程同时操作Promise,所以设置操作结果的时候需要加锁保护,防止并发操作。

对操作结果是否被设置进行二次判断(为了提升并发性能的二次判断),如果已经被设置,则返回操作失败。

对操作结果result进行判断,如果为空,说明仅仅需要notify在等待的业务线程,不包含具体的业务逻辑。因此,将result设置为系统默认的SUCCESS。如果操作结果非空,将结果设置为result。

如果有正在等待异步I/O操作完成的用户线程或其他系统线程,则调用notifyAll方法唤醒所有正在等待的线程。注意,notifyAll和wait方法都必须在同步块内使用。

 private boolean setSuccess0(V result) {
        if (isDone()) {
            return false;
        }

        synchronized (this) {
            // Allow only once.
            if (isDone()) {
                return false;
            }
            if (result == null) {
                this.result = SUCCESS;
            } else {
                this.result = result;
            }
            if (hasWaiters()) {
                notifyAll();
            }
        }
        return true;
    }


未完,待续。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值