一、多路复用
- 单线程可以配合 Selector 完成对多个 Channel 可读写事件的监控,这称之为多路复用。
- 多路复用仅针对网络 IO、普通文件 IO 没法利用多路复用。
- 如果不用 Selector 的非阻塞模式,线程大部分时间都在做无用功,而 Selector 能够保证
(1)、有可连接事件时才去连接;
(2)、有可读事件才去读取;
(3)、有可写事件才去写入;
(4)、限于网络传输能力,Channel 未必时时可写,一旦 Channel 可写,会触发 Selector 的可写事件;
二、Selector的概述
2.1、Selector的优点
- 一个线程配合 selector 就可以监控多个 channel 的事件,事件发生线程才去处理。避免非阻塞模式下所做无用功;
- 让这个线程能够被充分利用;
- 节约了线程的数量;
- 减少了线程上下文切换;
2.2、Selector的创建
//创建Selector,管理多个channel
Selector selector = Selector.open();
2.3、Selector绑定 Channel 事件
- Selector绑定 Channel 事件也称之为注册事件,绑定的事件 selector 才会关心。
- Selector绑定 Channel 事件语法
channel.configureBlocking(false);
/**
* 3、建立Selector与channel的联系(也称为注册)
* key只关注事件,绑定的事件类型4种,
* connect - 客户端连接成功时触发;
* accept - 服务器端成功接受连接时触发;
* read - 数据可读入时触发;
* write - 数据可写出时触发,
* */
SelectionKey key = channel.register(selector, 绑定事件);
2.4、Selector监听 Channel 事件
- 可以通过下面三种方法来监听是否有事件发生,方法的返回值代表有多少 channel 发生了事件。
- 方法1:阻塞直到绑定事件发生
int count = selector.select();
- 方法2:阻塞直到绑定事件发生,或是超时(时间单位为 ms)
int count = selector.select(long timeout);
- 方法3:不会阻塞,也就是不管有没有事件,立刻返回,自己根据返回值检查是否有事件
int count = selector.selectNow();
2.5、Selector何时不阻塞
- 事件发生时
(1)、客户端发起连接请求,会触发 accept 事件;
(2)、客户端发送数据过来,客户端正常、异常关闭时,都会触发 read 事件,另外如果发送的数据大于 buffer 缓冲区,会触发多次读取事件;
(3)、channel 可写,会触发 write 事件;
(4)、在 linux 下 nio bug 发生时; - 调用 selector.wakeup()
- 调用 selector.close()
- selector 所在线程 interrupt