IO 概述
IO 操作有如下几种:同步阻塞BIO、同步非阻塞NIO、异步非阻塞AIO。
- 在JDK1.4之前,只有BIO可用
- Java NIO(New IO 或 Non Blocking IO)是从JDK1.4引入的新 IO,可以代替标准的Java IO (BIO)。NIO是面向缓冲区的、基于通道的IO。NIO将以更加高效的方式进行IO的读写操作。
- AIO也就是NIO2,在JDK1.7中引入了NIO的改进版NIO2,AIO是异步非阻塞的IO模型
阻塞IO(BIO)
BIO是最传统的一种IO模型,即在IO操作(读写)的过程中会发生阻塞现象,直至IO操作完成。下面以C/S架构为例做简单介绍
- 在BIO模式中,服务器会为每个客户端连接创建一个线程,当有大量连接时,会大量占用服务器资源,直至服务器资源耗尽,导致服务器假死甚至奔溃。因此服务器在BIO模式下,不适用于高并发的场景。
- 在BIO模式中,服务器为提高性能都会采用线程池模型。服务器会创建一个固定(动态)大小的线程池。当有客户端请求时,从线程池中取出一个空闲线程来处理,处理完毕再释放回线程池。这可以避免大量线程的创建与销毁带来的资源浪费,使得线程得以重用。但是,当大量连接是长连接或同时有大量客户端连接时,可能会导致线程池中的线程全部被占用,没有空闲的线程去处理新的客户端连接,就会导致新的客户端连接失败。因此服务器在BIO模式下,即使采用线程池,不适用于长连接的场景,也不适用于高并发的场景。
非阻塞IO(NIO)
由于BIO的各种弊端,从JDK1.4开始出现了高性能IO模型非阻塞IO(NIO)。
- NIO基于Reactor模型,IO操作不会被阻塞。实现过程是:会为每个客户端注册感兴趣的事件,然后有一个线程专门去轮询每个客户端是否有事件发生,当有事件发生时,便顺序处理每个事件,当所有事件处理完之后,再继续轮询。
graph LR
1[client] --注册事件--> Thread((Thread <br> 轮询检测每个client是否有事件到达))
2[client] --注册事件--> Thread
3[client] --注册事件--> Thread
Thread --检测到有事件到达---> Event[处理事件1 <br>处理事件2 <br>处理事件3 <br><br>处理事件...]
Event --顺序处理完事件,<br>继续去轮询检测--> Thread
- NIO中实现非阻塞I/O的核心组件是Selector,在Selector中注册各种事件,当事件发生时执行相应回调。
graph
1[Socket] --- 4
2[Socket] --- 5
3[Socket] --- 6
4[SocketChannel] --register--> Selector
5[SocketChannel] --register--> Selector
6[SocketChannel] --register--> Selector
Selector --- Thread
- NIO模式,当建立一个连接时,不需要对应创建一个线程,这个连接会被注册到多路复用器上(Selector),一个Selector可以同时处理成千上万个连接,系统不需要创建大量的线程,大大减小了系统的开销。
| IO | NIO |
|---|---|
| 面向流(Stream Oriented) | 面向缓冲区(Buffer Oriented) |
| 阻塞IO(Blocking IO) | 非阻塞IO(Non Blocking IO) |
| 无 | 选择器(Selector) |
异步非阻塞IO(AIO)
- AIO也就是NIO2,在JDK1.7中引入了NIO的改进版NIO2,它是异步非阻塞的IO模型,是以事件驱动的,当客户端发送数据之后会主动通知服务端,接着服务端再进行读写操作。
- Java 的AIO是基于Proactor模式,与Reactor模式类似。Proactor和Reactor模式的主要区别是真正的读取和写入操作是由谁来完成的,Reactor中需要应用程序自身读取或写入数据,而Proactor中应用程序不需要进行实际的读写过程,它只需要从缓存区读取或者写入即可,操作系统会从缓存区进行真正的读写操作。