说道nio,不得不从io模型说起。
阻塞I/O模型
最常见的I/O模型是阻塞I/O模型,缺省情形下,所有文件操作都是阻塞的。我们以套接口为例来讲解此模型。在进程空间中调用recvfrom,其系统调用直到数据报到达且被拷贝到应用进程的缓冲区中或者发生错误才返回,期间一直在等待。我们就说进程在从调用recvfrom开始到它返回的整段时间内是被阻塞的。
非阻塞I/O模型
进程把一个套接口设置成非阻塞是在通知内核:当所请求的I/O操作不能满足要求时候,不把本进程投入睡眠,而是返回一个错误。也就是说当数据没有到达时并不等待,而是以一个错误返回。
I/O复用模型
系统提供select/poll,进程通过将一个或多个fd传递给select或poll系统调用,阻塞在select;这样select/poll可以帮我们侦测许多fd是否就绪。但是select/poll是顺序扫描fd是否就绪,而且支持的fd数量有限。系统还提供了一个epoll系统调用,epoll是基于事件驱动方式,而不是顺序扫描,当有fd就绪时,立即回调函数rollback。
信号驱动异步I/O模型
首先开启套接口信号驱动I/O功能, 并通过系统调用sigaction安装一个信号处理函数(此系统调用立即返回,进程继续工作,它是非阻塞的)。当数据报准备好被读时,就为该进程生成一个SIGIO信号。随即可以在信号处理程序中调用recvfrom来读数据报,井通知主循环数据已准备好被处理中。也可以通知主循环,让它来读数据报。
异步I/O模型
告知内核启动某个操作,并让内核在整个操作完成后(包括将数据从内核拷贝到用户自己的缓冲区)通知我们。这种模型与信号驱动模型的主要区别是:信号驱动I/O:由内核通知我们何时可以启动一个I/O操作;异步I/O模型:由内核通知我们I/O操作何时完成。
nio:服务器端保存一个Socket连接列表,然后对这个列表进行轮询,如果发现某个Socket端口上有数据可读时(读就绪),则调用该socket连接的相应读操作;如果发现某个 Socket端口上有数据可写时(写就绪),则调用该socket连接的相应写操作;如果某个端口的Socket连接已经中断,则调用相应的析构方法关闭该端口。这样能充分利用服务器资源,效率得到了很大提高。Java中使用Selector、Channel、Buffer来实现上述效果,如下图所示:
精简版:
详细版:
没有比较就没有伤害,再回过来看看bio。
bio:每建立一个Socket连接时,同时创建一个新线程对该Socket进行单独通信(采用阻塞的方式通信)。这种方式具有很高的响应速度,并且控制起来也很简单,在连接数较少的时候非常有效,但是如果对每一个连接都产生一个线程的无疑是对系统资源的一种浪费,如果连接数较多将会出现资源不足的情况。
他有以下两个缺点:
(a)使用一个独立的线程维护一个socket连接,随着连接数量的增多,对虚拟机造成一定压力;
(b)使用流来读取数据,流是阻塞的,当没有可读/可写数据时,线程等待,会造成资源的浪费
相比而言,nio利用selector轮询+线程的方式完美解决了bio的两大缺点!
Selector
Selector是Java NIO中用于管理一个或多个Channel的组件,控制决定对哪些Channel进行读写;通过使用Selector让一个单线程可以管理多个Channel甚至多个网络连接。
使用Selector最大的优势就是可以在较少的线程中控制更多的Channel。事实上我们可以使用一个线程控制需要使用的所有Channel。操作系统线程的运行和切换需要一定的开销,使用的线程越小,系统开销也就越少;因此使用Selector可以节省很多系统开销。
Channel
JavaNIO Channel和流有一些相似,但是又有些不同:
你可以同时读和写Channel,流Stream只支持单向的读和写。
Channel可以异步的读和写,流Stream是同步的。
Channel总是读取到buffer或者从buffer中写。
下面分别介绍一下Channel最重要的一些实现类:
FileChannel : 可以读写文件中的数据。
DatagramChannel:可以通过UDP协议读写数据。
SocketChannel:可以通过TCP协议读写数据。
ServerSocketChannel:允许我们像一个web服务器那样监听TCP链接请求,为每一个链接请求创建一个SocketChannel。
Buffer
在Java NIO中各类Buffer主要用于和NIO Channel进行交互,数据从Channel中读取到Buffer中,从Buffer写入到Channel中。我们可以将Buffer看做内存中的一块区域,我们可以在这块区域上写数据,然后在从中读取。这块内存区域被包装成NIO Buffer对象,提供了一系列的方法使我们操作这块内存变得更简单一些。
小结:
nio因为拥有了非阻塞的设计和利用selector的轮询方式,使得系统变得更加容易扩展,甚至可以把selector分布式化,在分布式网络编程中nio才是真正的如鱼得水,实现正真的高可用设计。Mina、Netty、Grizzly都是典型的例子。
加我微信进技术交流群,各大教学资源免费领取,大厂内推!关注订阅号:一只可爱的小码农,不定时推送高质量学习文章。