BIO、NIO、AIO,还傻傻分不清?

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

咱们知道java的I/O模型一共有四种,分别是:传统的BIO,伪异步I/O,NIO和AIO。前端

为了澄清概念和分清区别,咱们仍是先简单的介绍一下他们的概念,而后再去比较优劣。java

1.概念澄清

1.1 BIO

BIO,即Blocking I/O。面试

网络编程的基本模型是Client/Server 模型,也就是两个进程之间进行相互通讯,其中服务端提供位置信息(绑定的Ip 地址和监听端口) ,客户端经过链接操做向服务端监听的地址发起链接请求,经过三次握手创建链接,若是链接建在成功,双方就能够经过网络套接字( Socket ) 进行通讯。编程

在基于传统同步阻塞模型开发中, ServerSocket 负责绑定IP 地址,启动监听端口:Socket 负责发起链接操做。链接成功以后,双方经过输入和输出流进行同步阻塞式通讯。后端

BIO通讯模型图:服务器

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

解释一下上图:微信

采用BIO通讯模型的服务端,一般由一个独立的Acceptor线程负责监听客户端的链接,它接收到客户端链接请求以后为每一个客户端建立一个新的线程进行链路处理,处理完成以后,经过输出流返回应答给客户端, 统程销毁。这就是典型的一请求一回答通讯模型。网络

对于这种IO模型咱们知道:用户线程发出IO请求以后,内核会去查看数据是否就绪,若是没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用户线程交出CPU。当数据就绪以后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用户线程才解除block状态。即在读写数据过程当中会发生阻塞现象。并发

1.2 伪异步IO

为了解决同步阻塞 I/O 面临的一个链路须要一个线程处理的问题,后来有人对它的线程模型进行了优化一一后端经过一个线程池来处理多个客户端的请求接入,造成客户端个数M: 线程池最大线程数N 的比例关系,其中M 能够远远大于N。经过线程地能够灵活地调配线程资源,设置线程的最大值,防止因为海量并发接入致使线程耗尽。框架

伪异步IO通讯模型图:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

采用线程池和任务队列能够实现伪异步I/O通讯框架。当有新的客户端接入时,将客户端的Socket 封装成一个Task (该任务实现java.lang. Runnable 接口)投递到后端的线程池中进行处理, JDK 的线程将维护一个消息队列和N个活跃线程, 对消息队列中的任务进行处理。

因为统程池能够设置消息队列的大小和最大线程数,所以,它的资源占用是可控的, 不管多少个客户端并发访问, 都不会致使资源的耗尽和省机。

伪异步I/O 通讯框架采用了线程池实现,所以避免了为每一个请求都建立一个独立线程形成的线程资源耗尽问题。可是因为它底层的通讯依然采用同步阻塞模型,所以没法从根本上解决问题。

伪异步I/O 实际上仅仅是对以前I/O 线程模型的一个简单优化,它没法从根本上解决同步I/O 致使的通讯线程阻塞问题。下面咱们就简单分析下通讯对方返回应答时间过长会引发的级联故障。

  1. 服务端处理缓慢,返回应答消息耗费60s,平时只须要10ms;
  2. 采用伪异步I/O 的线程在读取故障服务节点的响应,因为读/取输入流是阻塞的,它将会被同步阻塞60s;
  3. 假如全部的可用线程都被故障服务器阻塞,那后续的全部的I/O消息都将在队列中排队;
  4. 因为线程地采用阻塞队列实现,当队列积满以后,后续入队列的操做将被阻塞;
  5. 因为前端只有一个Accptor 线程接收客户端接入,它被阻塞在线程池的同步阻塞队列以后,新的客户端请求消息将被拒绝,客户端会发生大量的链接超时;
  6. 因为几于全部的链接都超时,调用者会认为系统已经崩溃,没法接收新的请求消息。

如何破解这个难题?下面咱们再看一下NIO。微信搜索Java技术栈,在后台回复:面试,能够获取我整理的 Java/ IO 系列面试题和答案,很是齐全。

1.3 NIO

NIO,不少人叫他New I/O,因为以前老的I/O 类库是阻塞I/O ,New I/O 类库的目标就是要让Java 支持非阻塞I/O,因此,更多的人喜欢称之为非阻塞I/O(Non-block I/O)。

与Socket类和ServerSocket 类相对应, NIO也提供了SocketChannel 和ServerSocketChannel两种不一样的套接字通道实现。这两种新增的通道都支持阻塞和非阻塞两种模式。阻塞模式使用很是简单,可是性能和可靠性都很差,非阻塞模式则正好相反。开发人员能够根据本身的须要来选择合适的模式。

通常来讲,低负载、低并发的应用程序能够选择同步阻塞I/O以下降编程复杂度:对于高负载、高并发的网络应用,须要使用NIO 的非阻塞模式进行开发。

前面咱们已经对NIO进行了介绍,咱们知道NIO中引入了缓冲区Buffer,通道Channel和多路复用器Selector的概念。一个多路复用器Selector 能够同时轮询多个Channel,而Channel又是全双工的,同时支持读写操做,使用NIO 编程的优势总结以下:

  1. 客户端发起的链接操做是异步的,能够经过在多路复用器注册OP_CONNECT 等待后续结果,不须要像以前的客户端那样被同步阻塞。
  2. SocketChannel 的读写操做都是异步的,若是没有可读写的数据它不会同步等待,直接返回,这样I/O 通讯线程就能够处理其余的链路,不须要同步等待这个链路可用。
  3. 线程模型的优化:因为JDK 的Selector 在Linux 等主流操做系统上经过epoll 实现,它没有链接句柄数的限制(只受限于操做系统的最大句柄数或者对单个进程的句柄限制),这意味着一个Selector 线程能够同时处理成千上万个客户端链接,并且性能不会随着客户端的增长而线性降低。所以,它很是适合作高性能、高负载的网络服务器。
1.4 AIO

NIO 2.0 引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。

异步通道提供如下两种方式获取获取操做结果:

▷经过java.util.concurrent.Future 类来表示异步操做的结果;

▷在执行异步操做的时候传入一个java.nio.channels;

NIO 2.0 的异步套接字通道是真正的异步非阻塞I/O ,对应于UNIX 网络编程中的事件 驱动I/O (AIO) 。它不须要经过多路复用器( Selector) 对注册的通道进行轮询操做便可实 现异步读写,从而简化了NIO 的编程模型。

前面对不一样的I/O模型进行了简单介绍,不一样的I/O 模型因为线程模型、API 等差异很大,因此用法的差别也很是大。

咱们用一个表格来作一个统一说明:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

-END-

 

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

相关文章
相关标签/搜索