java nio通过selector实现单线程管理多连接,核心是事件驱动与非阻塞i/o:需用非阻塞通道注册关注事件,轮询就绪key并分类型处理读写连接,注意缓冲区管理、线程安全及资源释放。

Java NIO 中用 Selector 实现单线程管理多个网络连接,核心在于“事件驱动 + 非阻塞 I/O”。它让一个线程通过轮询就绪的通道事件(如可读、可写、已连接),避免为每个连接开一个线程,大幅降低资源开销。
创建并初始化 Selector 和 Channel
必须使用非阻塞模式的 SocketChannel 或 ServerSocketChannel,然后注册到 Selector 上,并指定关注的事件类型。
- 调用
Selector.open()获取选择器实例 -
ServerSocketChannel.open()创建服务端通道,设为非阻塞:configureBlocking(false) - 绑定端口后,用
register(selector, SelectionKey.OP_ACCEPT)注册接受连接事件 - 客户端通道同理,注册
OP_CONNECT、OP_READ等,注意连接建立成功后需手动切换关注OP_READ
在循环中轮询就绪事件
调用 selector.select()(或带超时的 select(timeout))阻塞等待事件;返回后遍历 selectedKeys() 处理就绪的 SelectionKey。
- 每次处理前建议用
iterator.remove()移除已处理的 key,否则下次轮询还会出现 - 通过
key.channel()获取对应通道,用key.attachment()可附加自定义对象(如用户会话、缓冲区) - 区分事件类型:
key.isAcceptable()处理新连接;key.isReadable()调用channel.read(buffer);key.isWritable()执行写操作
正确处理连接建立与读写逻辑
连接过程不是一步到位的,尤其客户端需手动完成连接确认;读写也要配合缓冲区管理和粘包/半包处理。
- 服务端收到
OP_ACCEPT后,用serverChannel.accept()获取新SocketChannel,设为非阻塞并注册OP_READ - 客户端注册
OP_CONNECT后,触发时要调用channel.finishConnect()判断是否真正连上,成功后再注册OP_READ - 读数据前清空
ByteBuffer(clear()),读完调用flip()切换读模式;写数据前确保缓冲区有内容且hasRemaining(),写完若未写完需继续关注OP_WRITE
注意线程安全与资源释放
Selector 本身不是线程安全的,所有注册、取消、事件处理都应在同一线程执行;通道和缓冲区也尽量避免跨线程共享。
- 关闭通道时,对应的
SelectionKey会自动失效;也可显式调用key.cancel(),再selector.wakeup()中断阻塞 - 不再使用时调用
selector.close(),它会关闭所有注册的通道 - 避免在事件处理器里做耗时操作(如数据库查询、文件读写),应交给线程池处理,防止阻塞整个事件循环
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











