事件循环线程阻塞在 select() 上时,别的线程怎么叫醒它?提前叫醒、重复叫醒、被中断、被关闭、注册新 channel 分别会发生什么?这篇逐项真实运行。
select() 阻塞 200 ms 后另一个线程 wakeup():select 在 1179 μs 内返回,返回值 0(没有就绪的 key) wakeup → select 返回的延迟(500 次):中位 30 μs,P90 37 μs,最大 231 μs 没有线程在 select 时先 wakeup():下一次 select(5000) 立刻返回(21 μs);再下一次 select(300) 正常阻塞了 302 ms(wakeup 的效果只保留一次) 连续 wakeup() 1000 次再 select:第 1 次 31 μs 返回,第 2 次阻塞 202 ms,第 3 次阻塞 200 ms(多次 wakeup 合并成一次) wakeup() 之后调用 selectNow(),再 select(200):阻塞了 202 ms(selectNow 清除了之前的 wakeup) 没有线程等待时 wakeup() 每次 31 ns(重复调用 1000000 次)
线程在 select() 里被 interrupt():312 μs 内返回(没有抛异常),返回后线程的中断标志 = true 带着中断标志进入 select 循环:300 ms 内 select() 空返回 392428 次(后 100 ms 有 141234 次)——和 park 一样,中断标志不清除时会变成忙等 另一个线程 close() 正在 select 的 Selector:select 在 3793 μs 内返回(select 返回 0,isOpen = false) 关闭之后再 select → ClosedSelectorException 关闭之后 wakeup() → 没有异常
select(2000) 阻塞时,另一个线程 register 一个监听 socket,随后有客户端连接上来:register 调用本身耗时 1599 μs(没有被阻塞);select 在 1895 ms 后返回,返回的就绪 key 数 = 0——等到超时才返回,新注册的 key 没有唤醒它 返回之后再 selectNow():就绪 key 数 = 1,isAcceptable = true 同样的场景,register 之后调用 wakeup():select 在 1.00 ms 后返回,就绪 key 数 = 0 已注册的 key,在 select 阻塞期间调用 interestOps(OP_ACCEPT)(此时已有客户端在等待连接):select 在 1892 ms 后返回,就绪 key 数 = 0