一、NIO 概述
- Java NIO(New IO 或 Non Blocking IO 非阻塞 IO)。
是从Java-1.4
版本开始引入一个 新的 IO API,可以替代标准的 Java IO API。NIO
支持面向 缓冲区、基于 通道 的 IO 操作。NIO
将以更加高效的方式进行 文件读写 操作。
1. 阻塞 IO
- 在进行同步
IO
操作时。
- 读取数据,会阻塞直至有可供读取的数据。
- 写入数据,将会阻塞直至数据能够写入。
- 传统的
Server/Client
模式,会基于 TPR(Thread per request 每个请求的线程)。
服务器 会为 每个客户端 请求建立一个线程,由该线程单独负责处理一个客户请求。
- 这种模式带来的一个问题,就是线程数量的剧增,大量的线程会增大服务器的开销。
- 大多数的使用为了避免这个问题,都采用了 线程池模型,并设置线程池线程的最大数量。
- 这由此带来了新的问题,如果线程池中有 100 个线程,而刚好有 100 个用户都在进行大文件下载,会导致第 101 个用户的请求无法及时处理,即便第 101 个用户只想请求一个几 KB 大小的页面。
- 传统的
Server/Client
模式如下图所示。
2. 非阻塞 IO (NIO)
- 什么是 Reactor 模式
- NIO 非阻塞 IO 采用了基于 Reactor模式 的工作方式。
IO 调用不会被阻塞,相反是 注册感兴趣的特定 IO 事件。
- 如 可读数据到达、新的套接字连接 等,在发生特定事件时,系统再通知我们。
- NIO 中实现 非阻塞IO 的核心对象就是 Selector。
Selector 就是注册各种 IO 事件地方。
而且当我们感兴趣的事件发生时,就是这个对象告诉我们所发生的事件。
- NIO 非阻塞 IO 如下图所示。
- 从图中可以看出,当有 读 或 写 等任何注册的事件发生时,可以从 Selector 中获得相应的 SelectionKey。
- 同时从 SelectionKey 中可以找到 发生的事件 和 该事件发生 的 SelectableChannel,以获得客户端发送过来的数据。
- 非阻塞 指的是 IO事件 本身不阻塞,但是获取 IO事件 的
select()
方法是需要阻塞等待的。
- 区别是 阻塞的IO 会阻塞在 IO 操作上,NIO 阻塞在事件获取上,没有事件就没有 IO,从高层次看 IO 就不阻塞了。
- 也就是说只有 IO 已经发生,才评估 IO 是否阻塞,但是
select()
阻塞的时候 IO 还没有发生,何谈 IO 的阻塞。- NIO 的本质是 延迟IO,操作要到真正发生 IO 的时候,而不是以前只要 IO流 打开了就一直等待 IO 操作。
3. IO 和 NIO 区别
- IO 和 NIO 对比。
4. NIO 三个核心组件
Channel
、Buffer
和Selector
构成了核心的API
。
4.1 Channel 通道
- Channel 可以翻译成 通道。
Channel 和 IO 中的 Stream(流)是差不多一个等级的。
- Stream 是单向的,如:
InputStream
,OutputStream
。- Channel 是双向的,既可以用来进行读操作,又可以用来进行写操作。
- NIO 中 Channel 的主要实现。
FileChannel
文件 IO。DatagramChannel
UDP。SocketChannel
TCP。ServerSocketChannel
Server/Client。
4.2 Buffer 缓冲区
- NIO 中 Buffer 的实现。
ByteBuffer
byte 类型。CharBuffer
char。DoubleBuffer
double。FloatBuffer
float。IntBuffer
int。LongBuffer
long。ShortBuffer
short。
4.3 Selector 选择器
Selector
运行单线程处理多个Channel
。
- 如果应用打开了多个通道,但每个连接的流量都很低,使用 Selector 就会很方便。
- 如在一个聊天服务当中,要使用 Selector,得向 Selector 注册 Channel,然后调用它的 select() 方法。
- 这个方法会一直阻塞到某个注册的通道有事件就绪。
一旦这个方法返回,线程就可以处理这些事件。- 事件的例子有:如新的连接进来、数据接收等。