BIO、NIO和AIO
BIO、NIO和AIO
首先来看一下什么是I/O
在一个通道中进行数据读写操作。
Java支持的三种网络编程模式:BIO,NIO,AIO
这三种模式采用了不同的通道模式进行数据传输。
1.BIO
同步并阻塞(传统阻塞型),服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销。
notice:
同步阻塞解释
这里的同步是从IO的角度来说的
IO分为两个部分,用户态和内核态,同步指->用户线程参与等待数据准备(内核中没有需要的数据,需要准备数据)<阻塞>和 等待数据从内核赋值到用户空间的过程<同步>。
即应用程序(JAVA代码)不能直接获取准备好的数据,而是需要等待数据的准备过程和等待数据从内核复制到用户内存空间,这就是阻塞;应用程序线程直接参与IO的操作,就是同步。
以上都是通过IO的角度看的,如果从调用者这一角度来看
客户端:也就是调用者,发送数据和接受数据都必须执行完成才能往下进行其他操作,所以此时对于客户端的调用者来说是同步的。
服务端:每个客户端请求对应一个服务端线程,一旦客户端接入建立连接后,服务端的这个线程就会一直被该客户端占用,即使客户端没有任何请求,服务端的这个线程也不会释放,服务端的这个线程也就是处于阻塞状态。
java中常用的例子:
socket通信,一个线程对应一个客户端连接。byte[] / char[],Stream。
2.NIO
同步非阻塞式IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
这个是大多数文章给出的介绍,但是我认为介绍的并不准确,因为从IO的角度看是不可能有同步非阻塞IO的。
所以我更喜欢称之为:新IO,多路复用IO,多路指的是多个Socket通道,复用指的是只用一个线程来管理它们,在JDK1.4之后提供。
notice:
n1.为什么说从IO的角度来看没有同步非阻塞IO
首先同步这点是可以认可的,应用程序直接参与IO操作,同步IO是指发起IO请求后,必须拿到IO的数据才可以继续执行,那么重点就在于非阻塞上了,多路复用IO。
image.png
通过多路复用的流程图可以发现,用户线程没有直接参与内核准备数据的过程,所以这里的非阻塞只是说在内核态数据准备的时候是非阻塞的,内核态将准备好的数据复制到用户态,应用层的时候依然是阻塞的。所以这个在IO的角度看来看,严格的来说依旧是同步阻塞的,而不是同步非阻塞。
n2.实际使用中客户端的使用情况
客户端:所有传输数据的操作都是异步的。
服务端:不再是一个客户端启动一个线程了,而是多路复用,多个客户端都注册到一个selector上,这个线程轮询,只有监听到有数据传输才被占用,当通道没有数据传输的时候,socket通道不占用多路复用器线程,所以是非阻塞的。
即 不再是一个socket通道对应一个服务端线程了,而是多个客户端共用一个服务端线程,当连接后的socket通道没有数据传输的时候,不会占用该线程,这个线程可以处理此时有数据传输的socket通道的业务。
image.png
java中常用的例子:
Netty框架通信;Buffer,Channel;
3.AIO
异步非阻塞式IO,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成后再通知服务器应用去启动线程进行处理。
BIO,NIO,AIO对比
BIO的伪异步模式
这里在提一个在BIO的基础上实现异步的伪异步的模式
由于BIO一个客户端需要一个线程去处理,因此我们进行优化,后端使用线程池来处理多个客户端的请求接入,形成客户端个数M:线程池最大的线程数N的比例关系,其中M可以远远大于N,通过线程池可以灵活的调配线程资源,设置线程的最大值,防止由于海量并发接入导致线程耗尽。
问题:
这种虽然实现了伪异步,可以同时处理多个客户端接入,达到提高并发的能力,但是依然存在问题,还是BIO的一个最大的问题,就是一个客户端接入后,对应一个服务端线程,即使这个客户端没有任何数据操作,依然会占用服务端的线程,无法释放。
BIO和NIO的对比
- 两种模式在处理客户端接入的处理方式不同,NIO采用多路复用模式。
- BIO 以流的方式处理数据,而 NIO 以块的方式处理数据,块 I/O 的效率比流 I/O 高很多
- BIO基于字节流和字符流进行操作,而 NIO 基于 Channel(通道)和 Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择器)用于监听多个通道的事件(比如:连接请求,数据到达等),因此使用单个线程就可以监听多个客户端通道
三种处理模式对比
第一种是阻塞IO,阻塞点有两个,等待数据就绪的过程和读取数据的过程(应用程序主动调用内核所以是同步)。
第二种是阻塞IO,阻塞点有一个,读取数据的过程(应用程序主动调用内核所以是同步)。
第三种是非阻塞IO,没有阻塞点,当工作线程启动时,数据已经(被系统)准备好可以直接用了(内核往用户空间拷贝完数据后,通知应用程序启动线程处理数据,所以是异步的,应用程序直接使用数据)。
还没有评论,来说两句吧...