SelectionKey.interestOpsAnd() 并非Java 11新增,而是JDK 1.4就存在的普通位运算方法,不保证线程安全、无原子性,仅等价于key.interestOps(key.interestOps() & ops),正确用法是由单一线程串行操作或外部同步保护。
`SelectionKey.interestO
ps
And()` 并**不是 Java 11 引入的新方法**,它早在 Java NIO 初版(JDK 1.4)就已存在,且**不涉及“原子操作”或“原子位运算”**。这是一个长期被误解的命名误导——名字里带 `And` 容易让人联想到并发安全的位运算,但实际它既**不保证线程安全**,也**不提供原子性保障**。
下面从三个关键角度帮你理清本质、避开陷阱:
它只是位掩码的普通按位与赋值
的作用是:将当前
的 interest set(监听事件集)与传入参数
执行
按位与(&)
,然后把结果重新设为新的 interest set。
等价于:
例如,当前监听
(即 0x0001 | 0x0004 = 0x0005),调用
后,监听集变为
,即只保留
—— 这是典型的“取消某个监听位”的惯用写法。
它完全不保证线程安全
本身
不是线程安全的对象
。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.”
—— 但唯独没说
方法可并发调用。
立即学习
“
Java免费学习笔记(深入)
”;
实践中,若多个线程同时调用
或
,会发生竞态条件:两个线程读取同一旧值 → 各自计算 → 先写入者的结果被后写入者覆盖 → 某个位变更丢失。
稿定在线PS
PS软件网页版
下载
✅ 正确做法:所有对同一
的
修改,必须由
单一线程串行执行
(通常是 selector 所在的 I/O 线程),或通过外部同步(如锁)保护。
Java 11 并未新增原子性支持,也不需要
Java 11 对 NIO 的改进聚焦于性能(如
大内存映射优化)、ZGC 支持、以及
在某些平台上的可扩展性提升,但
从未为加入 CAS 或 volatile 语义
。
原因很实际:NIO 设计哲学是“单线程驱动多路复用”,selector 线程天然串行处理事件和键状态变更。强行在
底层加原子操作,既破坏设计一致性,又无实际收益——反而增加无谓开销。
如果你真需要跨线程安全地调整监听状态,推荐模式是:
业务线程向 selector 线程提交一个
(通过
+ 队列)
selector 线程在下一轮
前统一执行所有 pending 的 interest 变更
不复杂但容易忽略:名字像原子操作,行为却很朴素。用对场景,比追求“看起来高级”更重要。
interestOpsAnd(int ops)SelectionKeyopskey.interestOps(key.interestOps() & ops);OP_READ | OP_WRITEkey.interestOpsAnd(~SelectionKey.OP_WRITE)0x0005 & ~0x0004 = 0x0001OP_READSelectionKeyinterestOps*interestOps()interestOpsAnd()SelectionKeyinterestOps*FileChannel.map()SelectorSelectionKeyinterestOpsAnd()Runnableselector.wakeup()select()