readyops() 表示通道当前真实就绪的事件,interestops() 表示用户主动注册关注的事件;二者通过位与运算协同,仅当 (readyops() & interestops()) != 0 时该键才被选中。

SelectionKey 的 readyOps() 和 interestOps() 是 Java NIO 中理解事件驱动模型的关键。它们不是普通整数,而是通过位掩码(bitmask)编码的整数值,分别表示“此刻通道已就绪可操作的事件”和“你主动注册希望监听的事件”。二者本质是独立的位集合,靠位运算关联——不是相等关系,也不是包含关系,而是“就绪事件必须是你关心的事件的子集”。
readyOps():当前真实发生的就绪事件
readyOps() 是由操作系统内核在 select/epoll/kqueue 等系统调用返回时填充的,反映的是底层文件描述符**此刻确实就绪**的状态。它只读,不能修改。常见取值包括:
-
SelectionKey.OP_READ(值为 1):通道有数据可读(如 SocketChannel 收到新包、ServerSocketChannel 有新连接待 accept) -
SelectionKey.OP_WRITE(值为 4):通道可写(通常对 SocketChannel 意味着发送缓冲区有空闲空间) -
SelectionKey.OP_CONNECT(值为 8):客户端连接操作完成(非阻塞 connect 的结果) -
SelectionKey.OP_ACCEPT(值为 16):服务端有新客户端连接请求到达
注意:readyOps() 可能同时包含多个事件(例如 1 | 4 = 5,表示既可读又可写),所以必须用位与(&)判断,不能用 ==。
interestOps():你主动声明的关注事件集合
interestOps() 是你通过 key.interestOps(int) 或 key.interestOps(int) | ... 显式设置的,代表你希望 Selector 在下次轮询中为你关注哪些事件。它是可变的,且仅影响后续的 select 操作。
- 初始值来自
channel.register(selector, ops)时传入的 ops - 修改它不会立即触发事件,只是告诉 Selector “下次检查时,请留意这些事件”
- 如果把
OP_WRITE加入 interestOps,但通道发送缓冲区一直未腾出空间,那readyOps()就永远不会包含 WRITE
二者如何通过位运算协同工作
Selector 的核心逻辑是:对每个 key,先检查其通道是否就绪,再确认就绪事件是否落在 interestOps 范围内。伪代码逻辑如下:
int ready = getReadyOpsFromKernel(); // 如 5(READ|WRITE)
int interest = key.interestOps(); // 如 1(只关心 READ)
if ((ready & interest) != 0) { // 5 & 1 == 1 ≠ 0 → 此 key 会出现在 selectedKeys() 中
// 处理就绪事件:需逐个判断 (ready & OP_READ) != 0 等
}
关键点:
- 就绪事件必须与兴趣事件按位与结果非零,该 key 才会被选中
- 即使
readyOps()返回了 WRITE,若interestOps()中没设 WRITE,这个就绪状态会被忽略 - 常犯错误:注册时只写
OP_READ,却在处理时误判readyOps() == OP_READ—— 应始终用(ready & OP_READ) != 0
实际使用中的典型操作模式
以 SocketChannel 写数据为例,说明二者配合的动态过程:
- 刚连接成功时:interestOps =
OP_READ,readyOps 可能含OP_CONNECT(需先 finishConnect) - 连接建立后:调用
key.interestOps(OP_READ),开始监听读事件 - 要发数据时:若 write() 返回 0(缓冲区满),则临时
key.interestOps(OP_READ | OP_WRITE),等待可写 - 收到 WRITE 就绪后:执行写操作;若写完还有剩余数据,继续监听 WRITE;否则恢复只监听 READ
整个过程依赖对两个位集合的精确控制和判断,而不是简单比对数值。











