1、简单介绍:
同步阻塞(blocking-IO
)简称BIO
同步非阻塞(non-blocking-IO
)简称NIO
异步非阻塞(asynchronous-non-blocking-IO
)简称AIO
-
BIO (同步阻塞I/O模式): 数据的读取写入必须阻塞在一个线程内等待其完成。这里使用那个经典的烧开水例子,这里假设一个烧开水的场景,有一排水壶在烧开水,BIO的工作模式就是, 叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。但是实际上线程在等待水壶烧开的时间段什么都没有做。
-
NIO(同步非阻塞): 同时支持阻塞与非阻塞模式,但这里我们以其同步非阻塞I/O模式来说明,那么什么叫做同步非阻塞?如果还拿烧开水来说,NIO的做法是叫一个线程不断的轮询每个水壶的状态,看看是否有水壶的状态发生了改变,从而进行下一步的操作。
-
AIO(异步非阻塞I/O模型): 异步非阻塞与同步非阻塞的区别在哪里?异步非阻塞无需一个线程去轮询所有IO操作的状态改变,在相应的状态改变后,系统会通知对应的线程来处理。对应到烧开水中就是,为每个水壶上面装了一个开关,水烧开之后,水壶会自动通知我水烧开了。
java
中的 BIO
、NIO
和AIO
理解为是 Java 语言
在操作系统层面对这三种 IO
模型的封装。程序员在使用这些 封装API 的时候,不需要关心操作系统层面的知识,也不需要根据不同操作系统编写不同的代码,只需要使用Java
的API就可以了。
2、详细说明
2.1 Java BIO
BIO
编程方式通常是是Java的上古产品,自JDK 1.0-JDK1.4就有的东西。编程实现过程为:首先在服务端启动一个ServerSocket
来监听网络请求,客户端启动Socket
发起网络请求,默认情况下SeverSocket
会建立一个线程来处理此请求,如果服务端没有线程可用,客户端则会阻塞等待或遭到拒绝。服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理。大致结构如下:
如果要让 BIO
通信模型能够同时处理多个客户端请求,就必须使用多线程(主要原因是 socket.accept()
、socket.read()
、 socket.write()
涉及的三个主要函数都是同步阻塞的),也就是说它在接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。这就是典型的 一请求一应答通信模型 。我们可以设想一下如果这个连接不做任何事情的话就会造成不必要的线程开销,不过可以通过线程池机制改善,线程池还可以让线程的创建和回收成本相对较低。使用线程池机制改善后的 BIO
模型图如下:
BIO
方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,是JDK1.4以前的唯一选择,但程序直观简单易懂。Java BIO
编程示例网上很多,这里就不进行coding举例了,毕竟后面NIO
才是重点。
2..2 Java NIO
NIO
(New IO或者No-Blocking IO),从JDK1.4 开始引入的非阻塞IO
,是一种非阻塞
+ 同步
的通信模式。这里的No Blocking IO
用于区分上面的BIO
。
NIO
本身想解决 BIO
的并发问题,通过Reactor模式
的事件驱动机制来达到Non Blocking
的。当 socket
有流可读或可写入 socket
时,操作系统会相应的通知应用程序进行处理,应用再将流读取到缓冲区或写入操作系统。
也就是说,这个时候,已经不是一个连接就 要对应一个处理线程了,而是有效的请求,对应一个线程,当连接没有数据时,是没有工作线程来处理的。
当一个连接创建后,不需要对应一个线程,这个连接会被注册到 多路复用器
上面,所以所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器
进行轮询的时候,发现连接上有请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。
NIO
提供了与传统BIO模型中的Socket
和ServerSocket
相对应的SocketChannel
和ServerSocketChannel
两种不同的套接字通道实现,如下图结构所示。这里涉及的Reactor
设计模式、多路复用Selector
、Buffer
等暂时不用管,后面会讲到。
NIO 方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局 限于应用中,编程复杂,JDK1.4 开始支持。同时,NIO
和普通IO的区别主要可以从存储数据的载体、是否阻塞等来区分:
2.3 Java AIO
与 NIO
不同,当进行读写操作时,只须直接调用 API 的 read
或 write
方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入 read
方 法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将 write
方法传递的流写入完毕时,操作系统主动通知应用程序。即可以理解为,read/write
方法都是异步的,完成后会主动调用回调函数。在 JDK7
中,提供了异步文件通道和异步套接字通道的实现,这部分内容被称作 NIO
.
AIO
方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用 OS
参与并发操作,编程比较复杂,JDK7
开始支持。
目前来说 AIO
的应用还不是很广泛,Netty
之前也尝试使用过 AIO
,不过又放弃了