java nio通过selector实现i/o多路复用,需将非阻塞的selectablechannel(如socketchannel)注册到selector,利用操作系统内核(如epoll)监听op_accept、op_connect、op_read、op_write四类事件,注册前须调configureblocking(false),filechannel不可注册;事件处理需遍历selectedkeys并手动remove,支持动态调整interestops。

Java NIO 通过 Selector(选择器)管理多路复用器的注册事件,核心是将多个非阻塞通道(Channel)统一注册到一个 Selector 实例,并由它监听各类 I/O 就绪事件。整个过程不是 Java 线程轮询,而是委托给操作系统内核(如 Linux 的 epoll),JVM 只负责接收通知、分发处理。
注册前必须设为非阻塞
只有实现了 SelectableChannel 接口的通道(如 SocketChannel、ServerSocketChannel、DatagramChannel)才能注册。
关键前提:调用 configureBlocking(false),否则注册时抛出 IllegalBlockingModeException。
FileChannel 不可注册——它不是 SelectableChannel 子类。
四类事件类型及对应通道
| 事件常量 | 适用通道类型 | 触发条件 |
|---|---|---|
OP_ACCEPT |
ServerSocketChannel |
有新客户端连接请求到达 |
OP_CONNECT |
SocketChannel |
客户端完成与服务端的 TCP 连接 |
OP_READ |
SocketChannel |
通道中存在可读数据(包括 EOF) |
OP_WRITE |
SocketChannel |
发送缓冲区有空闲空间(慎用!) |
注意:
OP_WRITE几乎总是就绪(只要 socket 发送缓冲区未满),盲目注册会导致 CPU 空转。推荐只在write()返回值小于待写字节数(即写半包)后临时注册,写完立即取消。
注册操作三步闭环
-
注册关注事件
SelectionKey key = channel.register(selector, SelectionKey.OP_READ, attachment);
-
attachment是可选对象(如ByteBuffer或自定义上下文),随SelectionKey一起返回,避免全局查表。 - 支持位或组合:
OP_READ | OP_WRITE。
-
-
等待内核通知
int n = selector.select(); // 阻塞,直到至少一个通道就绪 // 或带超时:selector.select(500); // 最多等 500ms
返回值
n表示本次就绪的SelectionKey数量。
Alibabacloud Sdk Client Initialization For Java下载在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
遍历并安全处理 selectedKeys
Set<selectionkey> selectedKeys = selector.selectedKeys(); Iterator<selectionkey> it = selectedKeys.iterator(); while (it.hasNext()) { SelectionKey key = it.next(); it.remove(); // 必须手动移除,否则下次 select() 还会返回它 if (key.isAcceptable()) { /* accept */ } if (key.isReadable()) { /* read */ } if (key.isWritable()) { /* write */ } if (key.isConnectable()) { /* finishConnect */ } }</selectionkey></selectionkey>-
严禁在遍历时直接调
selectedKeys.remove(key)或key.cancel(),会触发ConcurrentModificationException。 - 异常关闭或 I/O 错误时,应调
key.cancel()并显式关闭对应Channel,防止资源泄漏。
-
严禁在遍历时直接调
动态调整事件关注点
已注册的 SelectionKey 可随时修改兴趣集:
key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); // 重置为读+写 // 或追加:key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
适用于连接建立后从 OP_CONNECT 切换到 OP_READ,或写半包后临时加 OP_WRITE。
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










