写在开头:
本文不是纯讲技术,会涉及概念理解和语义分析的要点。正如博主标题说的,高效的学习方法比拥有多少知识重要。高效的学习方法途径之一就是阅读理解,理解能力越好,掌握越快,知识体系更清晰。
如果刚入门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操作
...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
基于上述的语境,用”不停顿等待”的词不够专业,于是很多人专业术语叫”异步”。然后叫”非阻塞”如何呢?其实也对啊,就是在执行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例子
---------------------
作者:matthew_zhang
来源:CSDN
原文:https://blog.csdn.net/matthew_zhang/article/details/71328697
版权声明:本文为博主原创文章,转载请附上博文链接!