1.前言

 第7节讲解JAVA的线程模型中就说到了Future,并解释了为什么可以主线程可以获得线程池任务的执行后结果,变成一种同步状态。秘密就在于Java将所有的runnable和callable任务,统一变成了callable,最终包装成了FutureTask对象,该类实现了Runnable接口和Future接口,所以FutureTask能够被线程执行。最终异步执行过程全部由该类控制逻辑,所以在get的时候锁住了该类,run方法执行的时候释放了锁,这样就满足了能够在异步线程执行完毕获取相关结果的能力。

 本章介绍一下Netty对Future的设计,Netty的声明就是一个异步事件驱动框架,上一节学习了整个线程调度的过程,并在最后给出了前几节的一个综合流程图,虽然图中提到了几种Future,但是没有具体介绍细节,这些将在本节得到解释。

2.相关概念

2.1 Future

 虽然Java中已经定义了Future,但是满足不了Netty的需求,所以Netty新写了一个Future接口,继承了JDK的Future。额外方法定义如下图:

 接口主要追加了两个功能:1.增加了判断任务是否成功失败的方法,以及失败获取异常信息;2.增加了任务完成时触发的监听器

2.2 Promise

 该类继承自Future,自然是增加了额外的功能了:这是一个可写的Future。什么意思呢?通过之前的知识,我们知道Future都是由异步线程控制的,主线程是无法控制线程执行的。Promise的作用就是主线程能够控制一下执行的任务。

  setSuccess():标记任务成功,并触发所有listener。如果任务早就成功或失败,则抛出异常

  trySuccess():同上,但是失败只是返回false,而不是抛出异常

 这里就解释这两个方法,其他方法是覆盖了父接口的方法,确定返回的具体类型而已。根据方法我们也能大体明白可写的含义了。

3 主要Future详解

3.1 DefaultChannelPromise

 该类是在注册channel时创建的,SingleThreadEventLoop的register方法。和Java的FutureTask不同,FutureTask是作为一个任务交给线程池,在内部控制任务执行。DefaultChannelPromise则是持有了Channel和EventExecutor二者,在外边处理逻辑。其上层有2个抽象父类:

  1.AbstractFuture:

    该类就实现了get方法,原理是调用了await()方法,await之后唤醒肯定就是任务结束了,判断有无异常,最终返回结果还是抛出异常。

  2.DefaultPromise:

    该类提供了一个Promise应该具备的基本实现。对任务标记结果,触发listener等。其主要有个result,对结果进行CAS操作来判断任务是否完成。

    setSuccess过程如下:1.设置成功的结果;2.触发所有的listener。设置结果主要是CAS更新result字段,然后判断是否有get请求等待任务执行完,直接notifyAll即可。触发listener的过程在于先判断当前线程是否是事件线程中,触发方法必须由EventLoop线程执行,然后就是遍历触发listener的operationComplete方法。

    await过程如下:1.判断是否执行完,执行完直接返回;2.判断线程是否中断,中断抛出异常;3.检查死锁,即wait操作不能在EventLoop的线程中执行;4.如果没执行完,等待者计数,然后wait。

 DefaultChannelPromise相对于DefaultPromise而言只是增加了一个channel字段,其它的方法都是调用父类方法。接下来我们看看register过程是怎么使用这个Promise的吧。

    public final void register(EventLoop eventLoop, final ChannelPromise promise) {
if (eventLoop == null) {
throw new NullPointerException("eventLoop");
}
if (isRegistered()) {
promise.setFailure(new IllegalStateException("registered to an event loop already"));
return;
}
if (!isCompatible(eventLoop)) {
promise.setFailure(
new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
return;
} AbstractChannel.this.eventLoop = eventLoop; if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
logger.warn(
"Force-closing a channel whose registration task was not accepted by an event loop: {}",
AbstractChannel.this, t);
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
}

 可以看到在注册过程中实际上就是使用setXXX方法来处理相关逻辑的,这个和Java的FutureTask采取了不同的方式。

3.2 SucceededFuture

 上面讲了一个Promise控制主线程和线程池的同步状态,那个是依靠promise才有的setXXX接口来触发的。那么Future是怎么控制的呢?答案是Future不需要控制,返回Future的时候就已经有结果了,并且返回一定是一个同步过程。

 以SucceededFuture为例。Bootstrap中的doResolveAndConnect0方法有段:final Future<SocketAddress> resolveFuture = resolver.resolve(remoteAddress);其解析成功就会返回带有结果的SucceededFuture。看这个类的sync方法也和Promise的不一样,Promise是await,Future是直接返回。这个可以说明Future和Promise的区别:Future用于同步任务,Promise用于异步任务。不知道Netty为什么会设计成这样,让人会有些疑惑。但是记住这点,再加上Promise的set方法达成的效果,就可以理解Netty的Future了。

4.总结

 Netty的Future设计采取了和Java的FutureTask不同的设计思路。Java的思路是将Futuren包装成一个任务,这样异步线程执行这个FutureTask的时候,其就可以知道任务的执行状态。Netty将Future扩展成了Promise。Future作为同步方法直接返回的结果类,使用较少。Promise提供了setXXX方法,给异步线程调用该方法告知执行状态。相同的地方在于Promise也必须被异步线程持有,才能使用set方法。

Netty核心概念(9)之Future的更多相关文章

  1. Netty In Action中文版 - 第三章:Netty核心概念

            在这一章我们将讨论Netty的10个核心类.清楚了解他们的结构对使用Netty非常实用.可能有一些不会再工作中用到.可是也有一些非经常常使用也非常核心,你会遇到. Bootstrap ...

  2. Netty核心概念(8)之Netty线程模型

    1.前言 第7节初步学习了一下Java原本的线程池是如何工作的,以及Future的为什么能够达到其效果,这些知识对于理解本章有很大的帮助,不了解的可以先看上一节. Netty为什么会高效?回答就是良好 ...

  3. Netty核心概念(7)之Java线程池

    1.前言 本章本来要讲解Netty的线程模型的,但是由于其是基于Java线程池设计而封装的,所以我们先详细学习一下Java中的线程池的设计.之前也说过Netty5被放弃的原因之一就是forkjoin结 ...

  4. Netty核心概念(10)之内存管理

    1.前言 之前的章节已经将启动demo中能看见的内容都分析完了,Netty的一个整体样貌都在第8节线程模型最后给的图画出来了.这些内容解释了Netty为什么是一个异步事件驱动的程序,也解释了Netty ...

  5. Netty核心概念(6)之Handler

    1.前言 本节介绍Netty中第三个重要的概念——Handler,这个在前两节都提到了,尤其是Channel和Handler联系紧密.handler本身的设计非常简单,但是所起到的作用却很大,Nett ...

  6. Netty核心概念(5)之Channel

    1.前言 上一节讲了Netty的第一个关键启动类,启动类所做的一些操作,和服务端的channel固定的handler执行过程,谈到了不管是connect还是bind方法最终都是调用了channel的相 ...

  7. Netty核心概念(4)之Bootstrap

    1.前言 第三节介绍了Netty的一些基本概念,此节介绍Netty的第一个概念Bootstrap——启动类.Netty中服务端和客户端的启动类是不一样的,这个不要搞错了,类都在bootstrap包下. ...

  8. Netty核心概念

    一个Netty程序始于Bootstrap类,Bootstrap类是Netty提供的一个可以通过简单配置来设置或“引导”程序的一个重要的类.Netty中设计了Handlers来处理特定的"ev ...

  9. 消息中间件——RabbitMQ(三)理解RabbitMQ核心概念和AMQP协议!

    前言 本章学习,我们可以了解到以下知识点: 互联网大厂为什么选择RabbitMQ? RabbiMQ的高性能之道是如何做到的? 什么是AMQP高级协议? AMQP核心概念是什么? RabbitMQ整体架 ...

随机推荐

  1. 删除k8s中一直处于Terminating的资源

    1.将所有的etcd中的key值取到一个keys.yam里面,便于查询 ETCDCTL_API=3 etcdctl get "" --from-key > keys.yaml ...

  2. Java 继承关系中:static,构造函数,成员变量的加载顺序

    首先看下面的例子: package simple.demo; /** * @author Administrator * @date 2019/01/03 */ public class ClassA ...

  3. java程序设计基本概念 jvm

    JVM (1)对于字符串:其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中.对于equals相等的字符串 ...

  4. JS数组去重算法实现

    之前一段时间一直在准备面试, 因而博客太久没更新: 现在基本知识点都复习完毕, 接下来就分享下 面试的一些常见问题: 去正规的互联网公司笔试.面试有很大的概率会碰到 使用javascript实现数组去 ...

  5. yum 常用命令

    yum是一个用于管理rpm包的后台程序,用python写成,可以非常方便的解决rpm的依赖关系.在建立好yum服务器后,yum客户端可以通过 http.ftp方式获得软件包,并使用方便的命令直接管理. ...

  6. Hdu1896 Stones(优先队列) 2017-01-17 13:07 40人阅读 评论(0) 收藏

    Stones Time Limit : 5000/3000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other) Total Submis ...

  7. mongodb 问题

    启动mongodb时,提示Unclean shutdown detected mongodb,解决方法很简单 mongod --repair --dbpath D:\MongoDB\blog   不用 ...

  8. The Real Meaning of Peace

    The Real Meaning of Peace There once was a king who offered a prize to the artist who would paint th ...

  9. 如何实现让你的网站支持Google Roboto Font

    最近在写一个移动端的web项目,designer设计的页面效果图就是使用Google Roboto Font.The Roboto Font 是Google为Android 4.0 Ice-Cream ...

  10. cocos2dx lua 图片去色shader

    function FreeCardTool:setNodeGray(node) local vertShaderByteArray = "\n".. "attribute ...