您的位置:首页 > 大数据 > 人工智能

异步和非阻塞一样吗? (内容涉及BIO,NIO,AIO,Netty)

2017-05-07 18:52 656 查看

写在开头:

本文不是纯讲技术,会涉及概念理解和语义分析的要点。正如博主标题说的,高效的学习方法比拥有多少知识重要。高效的学习方法途径之一就是阅读理解,理解能力越好,掌握越快,知识体系更清晰。

如果刚入门Netty,看过Netty线程模型,又看过JDK的NIO、NIO2,是否困惑于NIO,AIO,阻塞非阻塞的理解?

Netty目前4.0的版本应该是“非阻塞”的“同步IO”(按照Unix IO模型定义)。之前曾经有版本支持“异步IO”(按照Unix IO模型定义),但是因为某些原因被后来版本移除了。不管Netty是“同步IO”还是“异步IO”(按照Unix IO模型定义),其实我们应用的层面是不需要区分的,因为这是框架底层封装处理。我们应该仅仅关心是我的业务客户端代码调用Netty的API时候客户端程序是否会停顿等待。

“不停顿等待”

广义的异步,非阻塞,都可以直接理解为正在执行的线程“不停顿等待”.

当下面业务端main线程执行callNettyApi()时候,不管callNettyApi()执行是否完成,线程继续运行至callBusinessHandle(),通俗说就是该main方法的线程在执行callBusinessHandle()之前没有停顿等待callNettyApi()执行结果。

业务端程序:

void main(){

callNettyApi(); // 异步?非阻塞?

callBusinessHandle();

}

void callNettyApi(){
...
...//运行很长时间,例如IO操作
...
}


基于上述的语境,用”不停顿等待”的词不够专业,于是很多人专业术语叫”异步”。然后叫”非阻塞”如何呢?其实也对啊,就是在执行callNettyApi()方法时候没有阻塞main线程。

至此,作为普通应用开发者调用Netty的API,无非就是关心自己调用的API方法是否让自己的主线程停顿等待API执行结果,这种场景下是其实不需要考虑异步和非阻塞的区别的,叫异步也好,叫非阻塞也好。也就是广义的异步和非阻塞等同于”不停顿等待”。

“停顿等待”

广义的同步,阻塞,都可以直接理解为正在执行的线程“停顿等待”.

这个不详细举例说明了,就是上面例子的相反理解就行。

转入正题

文章开始就说了截止到Netty4.0版本是“非阻塞”的“同步IO”(按照Unix IO模型定义),同步与非阻塞矛盾?

答案是不矛盾。因为这句话要结合具体语境理解。

首先,在Unix的IO模型里:

异步I/O 是指用户程序发起IO请求后,不等待数据,同时操作系统内核负责I/O操作把数据从内核拷贝到用户程序的缓冲区后通知应用程序。数据拷贝是由操作系统内核完成,用户程序从一开始就没有等待数据,发起请求后不参与任何IO操作,等内核通知完成。

同步I/O 就是非异步IO的情况,也就是用户程序要参与把数据拷贝到程序缓冲区(例如java的InputStream读字节流过程)。

同步IO里的非阻塞 是指用户程序发起IO操作请求后不等待数据,而是调用会立即返回一个标志信息告知条件不满足,数据未准备好,从而用户请求程序继续执行其它任务。执行完其它任务,用户程序会主动轮询查看IO操作条件是否满足,如果满足,则用户程序亲自参与拷贝数据动作。





简单说,Unix IO模型的语境下,同步和异步的区别在于数据拷贝阶段是否需要完全由操作系统处理。阻塞和非阻塞操作是针对发起IO请求操作后是否有立刻返回一个标志信息而不让请求线程等待。基于这个语境,Netty目前的版本是没有把IO操作交过操作系统处理的,所以是属于同步的。对于网上大部分文章,如果别人说Netty是异步非阻塞,如果要深究,那真要看看Netty新的版本是否把IO操作交过操作系统处理,或者看看有否使用JDK1.7中的AIO API,否则他们说的异步其实是指客户端程序调用Netty的IO操作API“不停顿等待”

=====

其次,在Java的IO模型里:

JDK的各个历史版本引入如下概念。

BIO: Blocking IO

NIO:Non Blocking IO 或者 New IO

AIO:Asynchronous IO 或者 NIO2

这里的AIO应该就对应着Unix模型里的异步IO,也就是IO操作交给操作系统处理,充分调用OS参与并发操作,确实是操作系统的异步IO机制。

而BIO和NIO简单对比就是,NIO解决了BIO的痛点,把BIO中请求IO过程中的两步(请求连接+连接有真实IO请求时候的处理过程)分离开来,不让一个线程负责这两步。NIO就是一个线程负责所有请求连接但不处理IO操作,该线程只负责把连接注册到多路复用器上,多用复用器轮询到连接有IO请求时候再启动其它线程处理IO请求操作。

注意一点,Netty线程模型提及很多IO线程池,每条IO线程在进行IO操作(IO条件满足进行真正读写数据)时候虽然也是要消耗操作时间,但这种情况是否应该叫阻塞,取决于该IO线程有没有阻塞业务请求线程,当且仅当所有的IO线程在重度负载情形下(IO线程池所有IO线程在工作)导致业务请求线程提交不了新请求的情形下才叫IO线程的IO操作阻塞了业务线程的IO请求。由此可知,NIO也会产生BIO的情况。

启示

平时项目里,沟通效率低效,往往也是不同的人描述不同的东西时候用了相同的词汇,或者相同的东西却用了不同的词汇,这都会造成双方理解困难。要高效沟通,其中途径之一就是统一概念叫法。不允许重要而难懂的概念被随意指代。

延伸, 那么你是否好奇程序如何实现非阻塞的?

建议看看Java的Future模式实现和JDK并发包里的Future工具库。IO操作不阻塞用户请求线程,编程可以使用Future模式实现。

结束语

希望本文帮你理解清楚这些概念,然后随心应用Netty或者进行网络编程,无所顾虑自己的困惑影响到程序编写的正确性。

参考文章:

Java I/O模型从BIO到NIO和Reactor模式

BIO与NIO、AIO的区别(这个容易理解)

Netty系列之Netty线程模型

Netty中,耗时的业务逻辑代码应该写在哪?

JDK7+的NIO2.0中的AIO例子
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  jdk 异步 nio 线程
相关文章推荐