java nio中selector实现多路复用本质是统一轮询、按需响应,需通道为selectablechannel子类且设为非阻塞;流程包括创建selector、注册通道并设兴趣集、select()等待就绪、遍历selectedkeys处理事件并手动remove()。

Java NIO 中利用 Selector 实现多路复用,本质是让一个线程高效监听多个非阻塞通道的 I/O 事件。关键不在“同时处理”,而在“统一轮询、按需响应”——Selector 本身不执行读写,只负责告诉你“哪个通道在什么时候能安全地读或写”。
必须满足的前置条件
Selector 不接受任意通道,只有满足以下两点才能注册:
- 通道必须是 SelectableChannel 的子类(如
ServerSocketChannel、SocketChannel) - 通道必须调用
configureBlocking(false)设置为非阻塞模式,否则注册会直接抛出IllegalBlockingModeException
四步完成事件监听闭环
整个流程是固定且不可跳过的循环结构:
-
创建 Selector:调用
Selector.open(),底层会根据操作系统自动选择epoll(Linux)、kqueue(macOS/BSD)等高效实现 -
注册通道并指定兴趣集:对每个通道调用
register(selector, ops),ops是位或组合,例如OP_READ | OP_WRITE;注意ServerSocketChannel只关心OP_ACCEPT,而SocketChannel通常关注OP_READ或OP_WRITE -
调用 select() 阻塞等待就绪:该方法会挂起当前线程,直到至少一个注册事件就绪,或超时(可选),或被其他线程唤醒(
selector.wakeup()) -
遍历 selectedKeys 并逐个处理:从
selector.selectedKeys()获取就绪键集合,用key.isAcceptable()、key.isReadable()等判断类型;处理完后必须手动调用iterator.remove()或keyIterator.remove(),否则该 key 会在下一轮继续出现
事件类型与典型用途对应关系
不同事件代表不同网络阶段,不能混用:
-
OP_ACCEPT:仅用于
ServerSocketChannel,表示有新客户端发起 TCP 连接请求,此时应serverChannel.accept()得到新的SocketChannel,再将其注册进同一 Selector(通常只关注OP_READ) -
OP_CONNECT:仅用于客户端
SocketChannel,调用connect()后触发,表示 TCP 三次握手完成,可开始读写 - OP_READ:表示通道内核缓冲区有数据可读(不等于“一定有完整业务包”,需结合 Buffer 和协议解析)
- OP_WRITE:表示通道内核发送缓冲区有空闲空间,可以写入数据;它几乎总是就绪的,所以一般只在写操作因缓冲区满而返回 0 时才临时注册,写完立即取消,避免空转轮询
一个不能忽略的操作细节
每次循环中,selectedKeys() 返回的是一个共享的、可变的 Set,不是副本。这意味着:
- 必须用
Iterator遍历,并在处理完每个SelectionKey后调用remove(),否则该 key 会持续留在集合里,导致重复处理 - 不能在遍历过程中直接调用
selectedKeys().clear(),这会破坏 Selector 内部状态 - 如果某次处理中需要延迟响应(比如读到半包,等下次再读),不要移除 key,但要确保不陷入无限循环(例如限制单次最多读取次数)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











