SelectionKey.interestOpsAnd() 并非Java 11新增,而是JDK 1.4就存在的普通位运算方法,不保证线程安全、无原子性,仅等价于key.interestOps(key.interestOps() & ops),正确用法是由单一线程串行操作或外部同步保护。

它只是位掩码的普通按位与赋值
interestOpsAnd(int ops) 的作用是:将当前 SelectionKey 的 interest set(监听事件集)与传入参数 ops 执行 按位与(&),然后把结果重新设为新的 interest set。
等价于:
key.interestOps(key.interestOps() & ops);
例如,当前监听 OP_READ | OP_WRITE(即 0x0001 | 0x0004 = 0x0005),调用 key.interestOpsAnd(~SelectionKey.OP_WRITE) 后,监听集变为 0x0005 & ~0x0004 = 0x0001,即只保留 OP_READ —— 这是典型的“取消某个监听位”的惯用写法。
它完全不保证线程安全
SelectionKey 本身不是线程安全的对象。Javadoc 明确指出:“A key is valid only as long as its channel and selector are open.”,同时强调:“Keys may be safely cancelled at any time. Cancellation is safe even if the key is still in a selected-key set.” —— 但唯独没说 interestOps* 方法可并发调用。
立即学习“Java免费学习笔记(深入)”;
实践中,若多个线程同时调用 interestOps() 或 interestOpsAnd(),会发生竞态条件:两个线程读取同一旧值 → 各自计算 → 先写入者的结果被后写入者覆盖 → 某个位变更丢失。
✅ 正确做法:所有对同一 SelectionKey 的 interestOps* 修改,必须由单一线程串行执行(通常是 selector 所在的 I/O 线程),或通过外部同步(如锁)保护。
Java 11 并未新增原子性支持,也不需要
Java 11 对 NIO 的改进聚焦于性能(如 FileChannel.map() 大内存映射优化)、ZGC 支持、以及 Selector 在某些平台上的可扩展性提升,但 从未为 SelectionKey 加入 CAS 或 volatile 语义。
原因很实际:NIO 设计哲学是“单线程驱动多路复用”,selector 线程天然串行处理事件和键状态变更。强行在 interestOpsAnd() 底层加原子操作,既破坏设计一致性,又无实际收益——反而增加无谓开销。
如果你真需要跨线程安全地调整监听状态,推荐模式是:
- 业务线程向 selector 线程提交一个
Runnable(通过selector.wakeup()+ 队列) - selector 线程在下一轮
select()前统一执行所有 pending 的 interest 变更


















