Selector.wakeup() 是唯一安全、标准且线程安全的唤醒方式,用于中断阻塞在 select() 上的线程,需配合注册变更调用、及时清理 selectedKeys,并在优雅关闭时主动触发以确保线程及时退出。

Java NIO 中,Selector.wakeup() 是唯一安全、标准且线程安全的方式,用于唤醒正阻塞在 select() 或 select(long timeout) 上的线程。它不依赖锁、不中断线程、也不抛异常,而是通过内核事件机制协作式通知。
唤醒必须配合注册变更使用
多线程环境下,wakeup 的核心作用是“让 select 立即感知到用户态的注册变化”。例如工作线程刚调用 channel.register(selector, OP_READ),而主线程正在 select() 阻塞中——此时不唤醒,新通道永远不会被轮询到。
- 注册完成后,**立刻**调用
selector.wakeup(),无需判断 selector 是否正在阻塞(它是幂等的) - 不要等 select 自己超时或等到下一次事件才处理;否则会丢失事件窗口
- 避免在主线程 select 循环内部、尚未遍历完
selectedKeys()时调用 wakeup,以防干扰当前轮次 key 处理
唤醒后必须清理 selectedKeys
wakeup 只中断阻塞,**不会清空就绪键集合,也不会自动移除已处理的 key**。若忽略清理,会导致重复处理(如重复 accept、重复 read),甚至引发 ConcurrentModificationException。
- 每次
select()返回后,必须用Iterator遍历selectedKeys() - 对每个已处理的 key 调用
it.remove(),而不是用 for-each 循环 - 即使 wakeup 后
select()返回 0(无业务事件),也要检查并清空残留 keys(比如之前未处理完的)
关闭 Selector 时的唤醒配合
优雅关闭 selector 线程时,wakeup 是确保阻塞线程能及时退出的关键环节:
立即学习“Java免费学习笔记(深入)”;
- 先设置退出标志(如
running = false),再调用wakeup() - 在 select 循环中,每次返回后检查
selector.isOpen()和退出标志,满足则 break - 不要依赖
selector.close()自动唤醒——不同 JDK 版本行为不一致,应主动 wakeup - close() 应放在循环退出后执行,避免在阻塞中 close 引发未定义行为
避开常见误用陷阱
wakeup 是轻量但有开销的操作,滥用会退化为忙轮询,损害性能:
- 不要在 select 循环末尾无条件调用 wakeup(例如每轮都 call 一次)
- 不要在每次发送消息、每次写操作后都 wakeup——只在真正新增/修改注册状态时才需要
- 多次连续调用 wakeup 等价于一次,不会叠加;它靠内部布尔标志控制,不是计数器
- wakeup 对
wait()、sleep()、park()等完全无效,仅作用于 select 阻塞


















