java nio通过selector结合内核epoll实现单线程高并发,需channel设为非阻塞、显式注册事件、select()后遍历并remove selectedkeys,io线程仅做事件分发,耗时操作交业务线程池。

Java NIO 中用 Selector 实现单线程处理多路连接,核心是靠操作系统内核的 I/O 多路复用机制(如 Linux 的 epoll),而不是 Java 线程去轮询每个连接。Selector 不做“扫一遍所有连接”,而是等内核通知“哪些连接真有事了”,再集中处理——这样单线程轻松扛住上万并发。
必须先让 Channel 进入非阻塞模式
所有要注册到 Selector 的 Channel(比如 ServerSocketChannel、SocketChannel)都得调用 configureBlocking(false)。这是硬性前提:阻塞通道无法注册,否则抛 IllegalBlockingModeException。
- ServerSocketChannel 用于监听新连接,设为非阻塞后才能注册 OP_ACCEPT
- 新建立的 SocketChannel 同样要 configureBlocking(false),再注册 OP_READ(或后续加 OP_WRITE)
- 漏掉这一步,程序直接失败,不是性能问题,而是根本跑不起来
注册时明确指定关注的事件类型
注册 Channel 到 Selector 时,第二个参数必须传 interestOps,比如 OP_ACCEPT、OP_READ,不能只写 channel.register(selector)——那样等于没注册任何事件,这个 Channel 永远不会被唤醒。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 服务端主通道注册
OP_ACCEPT,用来接收新连接 - 每个客户端 SocketChannel 注册
OP_READ,表示“有数据来就叫我” - 需要主动发数据时,可临时改 interestOps 为
OP_WRITE,但注意 epoll 下 OP_WRITE 就绪条件宽松,慎用;更稳妥的是用 write() 尝试发送,写不完再关注 OP_WRITE
select() 阻塞等待,只处理真正就绪的连接
selector.select() 是阻塞调用,但它不是忙等,而是把控制权交给内核。内核在有任意注册事件就绪时才返回,此时 selectedKeys() 返回的只是那几十或几百个活跃连接,不是全部十万连接。
- 一次 select() 可能唤醒多个 Channel,但你只需遍历 selectedKeys() 集合,时间复杂度接近 O(K),K 是就绪数,不是总连接数 N
- 遍历 selectedKeys() 时,必须用迭代器的 remove() 方法手动清除已处理的 key;不 remove,下一轮 select() 还会把它列进来,导致重复处理甚至死循环
- 建议用
Iterator<selectionkey> it = selectedKeys.iterator(); while(it.hasNext()) { ... it.remove(); }</selectionkey>这种安全写法
业务逻辑别卡在 IO 线程里
Selector 所在的单线程只负责 IO 事件分发:收数据、发响应头、转发给业务线程。如果 decode、compute、DB 查询等耗时操作放在这里,整个轮询就会卡住,吞吐量断崖下跌。
- 读到完整请求后,把数据封装成任务,提交到业务线程池处理
- 写响应也尽量异步:把响应体写进 ByteBuffer,调用 channel.write();若未写完,保留 SelectionKey 并关注 OP_WRITE,等下次就绪再继续
- 单 Selector 的瓶颈往往不在连接数,而在 JVM 堆内存、GC 压力和业务代码执行时长
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










