单线程靠I/O多路复用支撑万级连接,关键在注册前设非阻塞、分通道注册事件、select后迭代移除key、ByteBuffer池化及避免selector线程阻塞。

单线程靠 Selector 管上万连接,不是靠“并发”,而是靠操作系统级的 I/O 多路复用(epoll / kqueue)把就绪事件“推”给 Java 层。关键不在轮子多快,而在注册、分发、清理三步是否闭环。
必须先调 configureBlocking(false),否则注册直接失败
几乎所有 IllegalBlockingModeException 都源于这一步遗漏。ServerSocketChannel 和 SocketChannel 都得在 register() 前设为非阻塞——不是“建议”,是 JVM 强制校验。
-
ServerSocketChannel忘设非阻塞 → 注册OP_ACCEPT时抛异常 -
SocketChannel忘设非阻塞 → 注册OP_READ或OP_WRITE时抛异常 - 注意:一旦 channel 关闭,再调
configureBlocking()会抛ClosedChannelException
OP_ACCEPT 和 OP_READ 必须分通道注册,混用必崩
ServerSocketChannel 只能注册 OP_ACCEPT;SocketChannel 才能注册 OP_READ / OP_WRITE。把两者写在同一行注册,key 会失效,后续 select() 返回 0 就绪数,服务静默卡死。
- 错误写法:
serverChannel.register(selector, SelectionKey.OP_ACCEPT | SelectionKey.OP_READ)→OP_READ对 server channel 无意义,key 的isValid()返回false - 正确流程:accept() 得到新
SocketChannel→ 立即configureBlocking(false)→ 单独注册OP_READ - 若需回写响应,不要长期注册
OP_WRITE,而是在 write() 返回值小于待写字节数时,临时追加OP_WRITE,写完立刻interestOps(interestOps & ~SelectionKey.OP_WRITE)取消
每次 select() 后必须用 Iterator.remove() 清理 selectedKeys
selectedKeys() 是个复用集合,不手动移除已处理的 key,下一轮 select() 还会把它再报一遍——导致重复处理、状态错乱、CPU 空转。
你是 TypeScript 项目架构专家,专注于脚手架搭建生产就绪的 Node.js 和前端应用。生成包含现代工具链(pnpm、Vite、Next.js)、类型
立即学习“Java免费学习笔记(深入)”;
- 绝对禁止在遍历时调
selectedKeys.remove(key)→ 抛ConcurrentModificationException - 必须用迭代器:
Iterator<selectionkey> it = selectedKeys.iterator(); while (it.hasNext()) { SelectionKey k = it.next(); ... it.remove(); }</selectionkey> - 如果处理逻辑可能耗时(如解码、DB 查询),别在 selector 线程里干——把 key 或附件对象(如
ByteBuffer)丢进业务线程池
万级连接下,ByteBuffer 不池化=内存爆炸或 GC 雪崩
每个连接配一个 ByteBuffer.allocateDirect(8192),1 万个连接就是 80MB DirectMemory;反复 new 堆内 buffer 又触发频繁 Young GC。JDK 自带的 buffer 没回收机制,必须自己管。
- 推荐用
ConcurrentLinkedQueue<bytebuffer></bytebuffer>+allocateDirect()池化,clear() 后归还,避免分配开销 - 慎用
ThreadLocal<bytebuffer></bytebuffer>:线程数少时有效,但 selector 线程只有一条,无法横向扩展 - buffer 容量不宜过大(如 >64KB),否则影响缓存局部性;也不宜过小(如
真正卡住万级连接的,往往不是 select() 调用本身,而是某条连接的读写逻辑没及时取消 interestOps、某次异常没 clean up key、或者 buffer 分配策略让 DirectMemory 耗尽后 OOM —— 这些点不显眼,但一出问题就是整条 selector 线程挂死。

















