4.NIO 的非阻塞式网络通信
传统的 IO 流都是阻塞式的。也就是说,当一个线程调用 read() 或 write()时,该线程被阻塞,直到有一些数据被读取或写入,该线程在此期间不能执行其他任务。因此,在完成网络通信进行 IO 操作时,由于线程会阻塞,所以服务器端必须为每个客户端都提供一个独立的线程进行处理,当服务器端需要处理大量客户端时,性能急剧下降。
Java NIO 是非阻塞模式的。当线程从某通道进行读写数据时,若没有数据可用时,该线程可以进行其他任务。线程通常将非阻塞 IO 的空闲时间用于在其他通道上执行 IO 操作,所以单独的线程可以管理多个输入和输出通道。因此,NIO 可以让服务器端使用一个或有限几个线程来同时处理连接到服务器端的所有客户端
4.1 选择器(Selector)
- 选择器(Selector) 是 SelectableChannle 对象的多路复用器,Selector 可以同时监控多个 SelectableChannel 的 IO 状况,也就是说,利用 Selector可使一个单独的线程管理多个 Channel。Selector 是非阻塞 IO 的核心。
- SelectableChannle 的结构如下图:
4.2 选择器(Selector)的应用
1. 创建 Selector :通过调用 Selector.open() 方法创建一个 Selector。
//创建选择器
Selector selecttor = Selector.open();
2. 向选择器注册通道: SelectableChannel.register(Selector sel, int ops);
//创建一个Socket 套接字
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9898);
//获取 SocketChannel
SocketChannel channel = socket.getChannel();
//创建选择器
channel.configureBlocking(false);
//向 Selector 注册 Channel
SelectionKey key = channel.register(seletor, SelectionKey.OP_READ);
-
当调用 register(Selector sel, int ops) 将通道注册选择器时,选择器对通道的监听事件,需要通过第二个参数 ops 指定。
-
可以监听的事件类型(可使用 SelectionKey 的四个常量表示):
- 读 : SelectionKey.OP_READ (1)
- 写 : SelectionKey.OP_WRITE (4)
- 连接 : SelectionKey.OP_CONNECT (8)
- 接收 : SelectionKey.OP_ACCEPT (16)
-
若注册时不止监听一个事件,则可以使用“位或”操作符连接。
//注册"监听事件"
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE
4.3 SelectionKey
SelectionKey:表示 SelectableChannel 和 Selector 之间的注册关系。每次向选择器注册通道时就会选择一个事件(选择键)。选择键包含两个表示为整数值的操作集。操作集的每一位都表示该键的通道所支持的