调用 selector.wakeup() 可主动唤醒阻塞的 select() 方法,其通过向内部 pipe 写入字节触发虚假就绪,使 select 立即返回 0;该操作线程安全、非阻塞,仅对当前或下次 select 有效,不中断系统调用也不抛异常。

Java NIO 中,Selector 的 select() 方法在没有就绪通道时会阻塞。要主动唤醒它,调用 selector.wakeup() 即可——这是线程安全的、非阻塞的唤醒机制。
唤醒原理:wakeup 是怎么起作用的
wakeup() 并不中断底层系统调用(如 Linux 的 epoll_wait),而是向 selector 内部的管道(Pipe)写入一个字节,触发一次“虚假就绪”。这会让正在阻塞的 select() 立即返回,且返回值为 0(表示本次没真正检测到就绪通道)。
注意:wakeup() 只对当前正在阻塞或即将进入阻塞的 select() 有效;如果 select() 没在运行,调用 wakeup() 会设置一个内部标志位,下次 select() 启动时立刻返回。
典型使用场景和调用位置
常见于多线程协作中,比如另一个线程需要注册新 Channel、修改 interest ops 或关闭 selector,而主线程正卡在 select() 上:
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...
- 在非 selector 线程中完成注册/取消操作后,立即调用
selector.wakeup() - 避免在 selector 线程里直接操作 key 集合(如
selectedKeys()),防止并发修改异常 - 如果已知 selector 正在 select,wakeup 能确保它尽快响应变更
注意事项和常见误区
wakeup() 不是“强制中断”,也不会抛出 InterruptedIOException 或导致 InterruptedException;它只是让 select 提前结束。
- 重复调用
wakeup()多次,只保证下一次select()被唤醒一次(内部标志位是二元的) - 调用
wakeup()后,不必担心“漏处理”——select 返回后仍会正常遍历 selectedKeys,你只需按需处理新增逻辑即可 - 不要在
select()循环内频繁 wakeup,否则可能退化为忙轮询;合理设计事件通知机制更关键
简单示例:唤醒后重新注册 channel
假设主线程在循环 select,另一线程想注册新 socket:
// 其他线程 SocketChannel newCh = SocketChannel.open(); newCh.configureBlocking(false); // 注意:不能直接 register 到 selector,需由 selector 线程执行 pendingRegistrations.add(newCh); // 放入共享队列 selector.wakeup(); // 唤醒 select,让主线程有机会处理注册
主线程 select 循环中检查队列并注册:
while (running) {
selector.select(); // 可能被 wakeup 打断
// 处理就绪事件...
Iterator<selectionkey> iter = selector.selectedKeys().iterator();
while (iter.hasNext()) { /* ... */ }
<pre class="brush:php;toolbar:false;">// 处理待注册 channel(在 selector 线程中安全注册)
for (SocketChannel ch : pendingRegistrations) {
ch.register(selector, SelectionKey.OP_READ);
}
pendingRegistrations.clear();
}
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










