NIO
什么是 NIO
java NIO是在JDK1.4 开始使用的,它既可以说成新 I/O (new io),也可以说成非阻塞 I/O(no-blocking io),NIO弥补了原来 I/O 的不足,它在标准java代码中提供了高速的 、面向块的 I/O 。
和BIO的区别
BIO:一个连接一个线程、面向流、流是阻塞的(当一个线程调用read(),或者write()时,该线程被阻塞,直至数据被读或者写完,期间该线程不能干任何事情)
NIO:一个请求一个线程、面向缓冲区、非阻塞(事件驱动,客户端发送的连接请求都会注册到多路复用器上,多路复用
器轮询到连接有 I/O 请求时才启动一个线程进行处理)
NIO 三大核心组件
Selector(选择器)
也可以称为 “ 轮询代理器”、“事件订阅器”等。
java NIO的选择器允许一个单独线程来监视多个输入管道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来操作这个选择器,进而选择通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个 单独的线程很容易来管理多个通道。
应用程序将向 Selector 对象注册需要它关注的 Channel,以及具体的某一个 Channel 会 对哪些 IO 事件感兴趣。Selector 中也会维护一个“已经注册的 Channel”的容器。
Channel (管道)
管道,建立的一个应用程序和操作系统交互事件、传递内容的管道(连接操作系统),既然是和操作系统进行内容传递,那么说明应用程序可以通过管道读取数据,也可以通过管道向操作系统写数据,而且可以同时进行读写。
- 所有被 Selector(选择器)注册的通道,只能是继承了 SelectableChannel 类的子类。
- ServerSocketChannel:应用服务器程序的监听通道。只有通过这个通道,应用程序才 能向操作系统注册支持“多路复用 IO”的端口监听。同时支持 UDP 协议和 TCP 协议。
- ScoketChannel:TCP Socket 套接字的监听通道,一个 Socket 套接字对应了一个客户 端 IP:端口 到 服务器 IP:端口的通信连接。
buffer 缓冲区(下篇单独介绍)
Buffer 用于和 NIO 通道进行交互。数据是从通道读入缓冲区,从缓冲区写入到通道中的。 以写为例,应用程序都是将数据写入缓冲,再通过通道把缓冲的数据发送出去,读也是一样, 数据总是先从通道读到缓冲,应用程序再读缓冲的数据。 缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存( 其实就是数组)。 这块内存被包装成 NIO Buffer 对象,并提供了一组方法,用来方便的访问该块内存。
SelectionKey
什么是SelectionKey
SelectionKey是一个抽象类,表示selectableChannel在Selector中注册的标识.每个Channel 向 Selector 注册时,都将会创建一个 SelectionKey。SelectionKey 将 Channel 与 Selector 建立了 关系,并维护了 channel 事件。
可以通过 cancel 方法取消键,取消的键不会立即从 selector 中移除,而是添加到 cancelledKeys 中,在下一次 select 操作时移除它,所以在调用某个 key 时,需要使用 isValid 进行 校验。
SelectionKey 类型和就绪条件
在向 Selector 对象注册感兴趣的事件时,JAVA NIO 共定义了四种:OP_READ、OP_WRITE、 OP_CONNECT、OP_ACCEPT(定义在 SelectionKey 中),分别对应读、写、请求连接、接受 连接等网络 Socket 操作。
操作类型 | 就绪条件及说明 |
---|---|
OP_READ | 当操作系统读缓冲区有数据可读时就绪。并非时刻都有数据可读,所 以一般需要注册该操作,仅当有就绪时才发起读操作,有的放矢,避免浪 费 CPU。 |
OP_WRITE | 当操作系统写缓冲区有空闲空间时就绪。一般情况下写缓冲区都有空 闲空间,小块数据直接写入即可,没必要注册该操作类型,否则该条件不 断就绪浪费 CPU;但如果是写密集型的任务,比如文件下载等,缓冲区很 可能满,注册该操作类型就很有必要,同时注意写完后取消注册。 |
OP_CONNECT | 当 SocketChannel.connect()请求连接成功后就绪。该操作只给客户端 使用。 |
OP_ACCEPT | 当接收到一个客户端连接请求时就绪。该操作只给服务器使用。 |
服务端和客户端分别感兴趣的类型
ServerSocketChannel 和 SocketChannel 可以注册自己感兴趣的操作类型,当对应操作类 型的就绪条件满足时 OS 会通知 channel,
下表描述各种 Channel 允许注册的操作类型,Y 表 示允许注册,其中服务器 SocketChannel 指由服务器 ServerSocketChannel.accept()返回的对象。
项目 | OP_READ | OP_WRITE | OP_CONNECT | OP_ACCEPT |
---|---|---|---|---|
服务器 ServerSocketChannel | Y | |||
服务器 SocketChannel | Y | Y | ||
客户端 SocketChannel | Y | Y | Y |
- 服务器启动 ServerSocketChannel,关注 OP_ACCEPT 事件
- 客户端启动 SocketChannel,连接服务器,关注 OP_CONNECT 事件
- 服务器接受连接,启动一个服务器的 SocketChannel,这个 SocketChannel 可以关注 OP_READ、OP_WRITE 事件,一般连接建立后会直接关注 OP_READ 事件
- 客户端这边 SocketChannel 发现连接建立后,可以关注 OP_READ、OP_WRITE 事件,一般是需要客户端需要发送数据了才关注 OP_READ 事件
- 连接建立后客户端与服务器端开始相互发送消息(读写),根据实际情况来关注OP_READ、 OP_WRITE 事件