java nio selector 实现单线程高并发的核心是“注册—轮询—处理—动态调整”四步闭环:通道须设为非阻塞才能注册;按连接状态动态设置op_accept、op_connect、op_read、op_write;select()后须用iterator遍历selectedkeys并remove();interestops需实时增删;依场景选用select()、select(timeout)或selectnow()。

Java NIO 中用 Selector 管理多个通道事件,核心是“注册—轮询—处理—动态调整”四步闭环。它不是为每个连接开一个线程,而是让一个线程通过事件就绪通知来驱动 I/O,真正实现单线程高并发。
注册通道前必须设为非阻塞
所有要交给 Selector 管理的 Channel(如 ServerSocketChannel、SocketChannel)都得先调用 configureBlocking(false)。否则注册会直接抛出 IllegalBlockingModeException。这是硬性前提,不是可选项。
- ServerSocketChannel 注册时通常关注 OP_ACCEPT,用于接收新连接
- SocketChannel 连接建立后,注册关注 OP_READ;若需主动发数据,再按需加 OP_WRITE
- 客户端 SocketChannel 初始注册常带 OP_CONNECT,连接成功后要立刻取消它,改用 OP_READ
select() 轮询与 selectedKeys 处理
调用 selector.select() 会阻塞,直到至少一个注册通道有事件就绪。返回值是就绪通道数,但真正要操作的是 selector.selectedKeys() 返回的 Set。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 必须用 Iterator 遍历并手动 remove() 每个已处理的 key —— 否则下次 select() 会重复返回它
- 不要在遍历过程中修改 selectedKeys 集合本身(比如 add/remove),只对 key 做操作
- 常见写法:用 key.isAcceptable()、key.isReadable() 等判断类型,再分发处理逻辑
动态调整 interestOps 是关键动作
一个 channel 的关注事件不是注册完就固定不变的。实际场景中需要实时增删:
- 刚完成 connect(OP_CONNECT 就绪)后,应立即执行 key.interestOps(SelectionKey.OP_READ),停掉连接监听
- 写数据时如果 channel.write(buf) 返回 0 或抛 IOException,说明 TCP 发送缓冲区满,此时应 key.interestOps(key.interestOps() | OP_WRITE) 加回写关注
- 写完一批数据后,若缓冲区还有剩余未发送完,可暂时 清除 OP_WRITE,避免空转轮询
- 该操作线程安全,但强烈建议只在 selector 所在的事件循环线程里做,避免竞态
selectNow() 和超时 select 的适用时机
不是所有场景都要阻塞等待。根据业务节奏选择合适的方法:
- select():适合纯事件驱动服务,希望 CPU 零空转,有事才干活
- select(long timeout):适合需要定期检查其他状态(如定时任务、心跳检测)的混合型服务
- selectNow():适合嵌入主循环、要求低延迟响应的场景,比如游戏服务器或实时风控模块,它立刻返回当前就绪数,不挂起线程
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










