Selector 是 Java NIO 中单线程管理多通道的核心机制,要求通道必须为非阻塞模式,通过 register() 注册事件并使用 select() 轮询就绪键,需及时移除已处理的 SelectionKey 并合理利用 attachment 保存通道状态。

Selector 是 Java NIO 中实现单线程管理多个通道(Channel)的核心机制,它让一个线程能高效轮询多个已注册的 Channel 的 I/O 事件(如连接就绪、读就绪、写就绪),避免为每个连接创建独立线程,显著降低资源开销。
前提:通道必须是非阻塞模式
只有非阻塞通道(SocketChannel、ServerSocketChannel、DatagramChannel 等)才能注册到 Selector。阻塞通道调用 register() 会直接抛出 IllegalBlockingModeException。
设置方式示例:
ServerSocketChannel serverChannel = ServerSocketChannel.open(); serverChannel.configureBlocking(false); // 必须设为 false
创建 Selector 并注册通道
通过 Selector.open() 创建实例;通道通过 channel.register(selector, ops, attachment) 注册,其中 ops 是感兴趣的事件掩码(如 SelectionKey.OP_ACCEPT、OP_READ)。
常见操作组合:
-
服务端监听通道:注册
OP_ACCEPT -
已建立的客户端通道:注册
OP_READ(通常不立即注册OP_WRITE,除非有数据待发) -
需要主动写入时:可临时增加
OP_WRITE,写完后建议移除,防止空轮询
事件循环:select + 处理键集
核心是调用 selector.select()(阻塞等待)或 selectNow()(非阻塞立即返回),然后遍历 selectedKeys() 集合处理就绪事件。
关键注意事项:
- 每次处理完一个
SelectionKey后,必须调用key.remove()或在迭代时使用Iterator.remove(),否则该键会在下次select()中重复出现 - 不要在处理过程中直接修改集合(如边遍历边
remove()键),应使用Iterator安全删除 - 若通道异常关闭或发生 I/O 错误,需手动取消键:
key.cancel(),并关闭对应通道
附带状态与附件(Attachment)的合理使用
注册时传入的 attachment 对象(如 ByteBuffer、自定义上下文对象)可在事件处理时直接获取,避免额外查找或全局映射。
例如:
ByteBuffer buffer = ByteBuffer.allocate(1024); channel.register(selector, SelectionKey.OP_READ, buffer); // 在事件处理中: ByteBuffer buf = (ByteBuffer) key.attachment();
这样既解耦又高效,也利于维护每个通道独立的读写缓冲区和业务状态。

















