BIO,NIO和AIO模型介绍

在计算机的世界中,IO操作是不可避免的一个话题。

IO操作涉及到的阻塞非阻塞同步异步这些概念常常让我感到混乱,为此,专门抽出时间对这些概念做了一下简单的研究,记录如下。希望可以帮助还在这些概念中挣扎的同学。

阻塞,非阻塞,同步和异步

IO操作实际上可以分为两步:发起IO请求实际的IO操作。如果在第一步发起IO请求时发生阻塞,那么这个IO操作就可以说阻塞的,否则是非阻塞的。如果在第二步实际IO操作时发生阻塞,那么这个IO操作就是同步的,否则就是异步的。

换种说法,阻塞和非阻塞是指在用户程序查询IO就绪状态时(比如查询IO是否有数据),用户程序对IO不同的就绪状态所表现出来的不同形式。以读取数据为例,当IO没有数据可供读取时,如果是阻塞IO,程序会一直阻塞直至IO有数据,如果是非阻塞IO,程序会直接返回错误码说当前没有数据,请稍后再来查询。同步和异步是由在进行实际的IO操作时,用户程序是否等待数据操作完成来决定。还是以读取数据为例,如果是同步IO,用户程序会等待读取数据完成,在此期间这个线程什么也不做,如果是异步IO,用户程序可以去作别的事情,内核在完成数据读取后,会以回调的方式通知用户程序。

同步阻塞IO,同步非阻塞IO,IO多路复用和异步IO模型

在Unix中,共有五种IO模型,阻塞IO,非阻塞IO,IO多路复用和信号驱动IO以及异步IO,Java中实现了除信号驱动IO外的其他四种,以下图片来自这里

同步阻塞IO

同步阻塞IO模型相对比较简单,操作流程如下,从用户发起read请求,到数据从IO读取至用户buffer,整个过程中用户线程始终处于阻塞状态,无法做别的事情。 bio

同步非阻塞IO

同步非阻塞IO模型的操作流程如下,用户发起read请求,这时候如果IO数据没有准备好,那么read函数立即返回,用户线程需要不停的去轮询是否有数据到来,当有数据到来时,用户程序再次发起读取数据操作,这个时候用户线程会发生阻塞,直至数据从IO拷贝至buffer操作完成,用户线程继续处理读取到的数据,这也是同步的意义所在。 nio

IO多路复用

IO多路复用本质上还是属于同步非阻塞IO模型,不同点在于它通过selector实现在一个线程中监听多路通道数据。IO多路复用的操作流程如下,用户线程首先需要把需要监听的socket注册至selector,然后selector负责去轮询各个socket通道是否有数据到来,一旦有数据到来,select返回,最后用户程序读取IO数据,这个过程仍然是用户线程阻塞直至数据读取完成。需要注意的是,这里的socket通道必须配置为非阻塞,这样select才能去依次轮询所有注册在selector中的socket通道。
nio_seletor IO多路复用采用Reactor设计模式,操作流程如下。用户线程需要首先在Reactor中注册一个事件处理器,然后Reactor(相当于上文提到的selector)负责轮询各个通道是否有新的数据到来,当有新的数据到来时,Reactor通过先前注册的事件处理器通知用户线程有数据可读,此时用户线程向内核发起读取IO数据的请求,用户线程阻塞直至数据读取完成。
nio_reactor

异步IO

最后是异步IO。异步IO采用Proactor设计模式,操作流程如下,跟Reactor模式一样,用户线程首先也需要向Proactor注册一个事件处理器,然后告诉内核要读取IO数据,这个时候用户线程就去做别的事情了,剩下监听IO数据以及IO数据的读取都由内核来完成,完成之后内核通过用户线程注册的事件处理器通知用户线程,“数据已经读取完成,你可以对这些数据做你自己的处理了”,最后用户线程拿到数据开始做自己的处理。 aio

上面分析了阻塞,非阻塞,同步和异步的区别以及各种IO模型的操作流程,下篇文章中会用实际的例子来说明在Java中各个IO模型的使用。

参考

JAVA中IO技术:BIO、NIO、AIO

高性能IO模型浅析

Shaohang Zhao

Read more posts by this author.