java nio selector通过操作系统内核的i/o多路复用(如epoll/kqueue/iocp)实现单线程管理多channel,本质是内核通知就绪事件,jvm仅收通知、分发任务;通道必须configureblocking(false)才能注册,且需严格遵循注册→select()等待→遍历selectedkeys()并手动remove()的闭环流程。

Java NIO 中 Selector 实现单线程管理多路复用,本质是把“轮询多个连接”这件事交给操作系统内核来做,JVM 线程只负责收通知、分发任务。它不靠 Java 层循环检查每个 Channel,而是依托底层 epoll(Linux)、kqueue(macOS)或 IOCP(Windows 模拟)等机制,让一个线程监听成百上千个非阻塞 Channel 的就绪事件。
必须用非阻塞通道
只有 configureBlocking(false) 的 Channel 才能注册到 Selector。阻塞通道注册会直接抛 IllegalBlockingModeException。常见通道如 ServerSocketChannel、SocketChannel、DatagramChannel 都支持,但 FileChannel 不可注册——它不是 SelectableChannel 的子类。
- ServerSocketChannel 启动后立即设为非阻塞,并只注册 OP_ACCEPT
- accept() 得到的 SocketChannel 也要先 configureBlocking(false),再注册 OP_READ(或按需临时加 OP_WRITE)
- 注册前未设非阻塞,是引发异常的最常见原因
注册 + 等待 + 处理三步闭环
Selector 的工作流固定为:注册关注事件 → 调 select() 等内核通知 → 遍历 selectedKeys() 分发处理。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 注册用 channel.register(selector, ops, attachment),其中 ops 是位掩码,如 OP_READ | OP_WRITE
- select() 默认阻塞,也可传毫秒超时(如 select(100)),避免无限等待
- 每次 select() 返回后,selectedKeys() 包含本次就绪的 SelectionKey,必须用 Iterator 安全遍历并调 remove()
关键细节决定是否稳定
很多问题不是逻辑错,而是忽略几个硬性约束:
- 处理完一个 key 后不调 keyIterator.remove(),下次 select() 还会返回它,导致重复处理
- 在遍历 selectedKeys() 时直接调 key.cancel() 或集合的 remove(),会触发 ConcurrentModificationException;必须用迭代器的 remove()
- OP_WRITE 几乎总是就绪(只要发送缓冲区有空),滥用会导致 CPU 空转;只应在 write 返回 -1(写半包)后显式关注,写完立刻取消
- 异常关闭或 I/O 错误时,应调 key.cancel() 并显式关闭对应 Channel,防止资源泄漏
附件(attachment)提升状态管理效率
注册时传入的 attachment 对象(比如 ByteBuffer、自定义 Context)会在事件触发时随 SelectionKey 一起返回,避免全局查找或 Map 映射。
- 例如:channel.register(selector, OP_READ, ByteBuffer.allocateDirect(4096))
- 在 isReadable() 分支中,直接 (ByteBuffer) key.attachment() 获取专属缓冲区
- 这对万级连接特别重要——每个连接独享 buffer,又不用反复 new 堆内存
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










