BIO、NIO以及AIO特点及比较
BIO
传统阻塞式IO的瓶颈在于每个连接都要创建线程,当连接过多时不能处理大量连接。
讨论为什么传统IO会阻塞?
首先说明一下socket
socket是支持TCP/IP网络通信的基础,是网络通信端点的抽象表示。
可以使用5元组表示socket。为 协议,源ip,源port,目标ip,目标port。
协议可选TCP或UDP,是TCP/IP协议的封装和应用。socket本身不是一个协议,而是一个调用接口(API),通过socket,才可以更方便使用TCP/IP协议。也就是说,socket只是为了应用层能更方便使用TCP/IP协议的一个编程接口。
在系统内核,有关于socket的2个缓存区比较重要。分别是socket的读缓存区(read buffer)和写缓冲区(write buffer)。
流程如下:当调用java BIO的write()
方法写数据时,实际上调用的是底层socket。写到socket的写缓冲区(write buffer)。(注意当调用write()
方法返回时,不代表数据已经发送出去,此时仅仅写入了socket的write buffer而已)。操作系统有一个专门的线程用于检查写缓冲区,有数据后将数据送入网卡设备经网络传送出去。
同理,对于读缓存区也一样。当调用系统的read()
方法时,是从socket的读缓冲区读数据。(系统内核有线程会将收到的数据拷贝到socket读缓冲区供用户空间使用)
两个缓冲区的大小是有限的,当写缓冲区写满后,会阻塞写操作,当读缓冲区为空后,会阻塞读操作。这是阻塞的根源。
所以总结来说:
- 在我们使用中调用的
read/write
方法实际上是从缓冲区读、写数据而已,对于写,它只是将数据写到缓冲区去了,至于什么时发送,由操作系统决定。对于读,只是从socket缓存区读取数据而已,读取不到指定大小就会阻塞。 - 对于阻塞读而言,read的原理是 没有数据会一直等待,有数据则会返回(若多于指定大小,会返回指定大小,若小于指定大小,则能返回多少返回多少)。
当使用IO读取数据时,实际上底层使用的是socket操作的数据。读取时读取socket的 read buffer。若socket读缓冲区没有足够的数据时,读取操作就会阻塞,直到有了足够数据。
NIO
java NIO 主要3部分: channel,buffer,selector。分别是通道,缓冲区和选择器。
channel是通道,也即数据流通的抽象,这里比传统java IO的流相对应,但功能更强。channel是双向的,可读可写,而流是单向的,分输入流或输出流。
AIO
AIO的思想是进程开始发起很多IO操作,不用阻塞或等待操作完成,可以稍后查看操作结果或等待IO完成通知。
之前的BIO中,一个进程在读取或写入数据时,若数没有准备好,则会阻塞进程直到完成,而AIO则不会,若操作没有完成,可以进行其他操作不会阻塞,同时,可选择若此操作完成后,异步通知或写Future
对于NIO和AIO的区别,NIO是若操作没准备好,监视操作并当操作准备好后通知处理者处理。而AIO则是,若操作没准备好,写入异步回调或写入Future。