Java NIO非阻塞网络编程核心在于注册、轮询、处理、清理四步闭环:通道须设为非阻塞,正确注册对应OP事件,select后遍历并安全移除selectedKeys,读写需适配返回值语义。

Java NIO 的非阻塞模式配合 Selector 实现网络编程,本质是用一个线程轮询多个通道的就绪状态,避免线程阻塞和资源浪费。关键不在“能不能写”,而在“怎么注册、怎么轮询、怎么处理、怎么清理”这四个动作是否闭环。
非阻塞通道必须提前配置
Channel 默认是阻塞的,不显式设置就会卡死 Selector。所有参与多路复用的通道(如 ServerSocketChannel、SocketChannel)都必须调用 configureBlocking(false)。
- ServerSocketChannel 注册 OP_ACCEPT 前必须非阻塞,否则 accept() 会阻塞整个线程
- SocketChannel 接收后也要设为非阻塞,才能注册 OP_READ 或 OP_WRITE
- FileChannel 不支持注册到 Selector,它本身就不走事件驱动路径
Selector 要正确注册并监听对应事件
每个 Channel 只能注册一次到同一个 Selector,但可以修改 interest set(比如读完数据后临时加 OP_WRITE)。注册时指定的事件类型决定后续哪些就绪状态会被捕获:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- OP_ACCEPT:仅 ServerSocketChannel 使用,表示有新连接待 accept()
- OP_READ:SocketChannel 有数据可读(注意:可能只读到部分,需循环 read() 直到返回 -1 或 0)
- OP_WRITE:慎用——它几乎总是就绪,除非发送缓冲区满;通常用于恢复因 write() 返回 0 而中断的写操作
- OP_CONNECT:客户端 SocketChannel 连接建立完成时触发
select() 调用与 selectedKeys 处理要成对出现
select() 是阻塞点,但它的阻塞是有意义的——等内核通知事件。调用后必须遍历 selectedKeys() 并逐个处理,且每次处理完必须手动 remove(),否则下次 select() 仍会返回该 key,导致重复处理或内存泄漏。
- 用 iterator.remove() 安全移除,不要用集合的 remove() 方法
- 如果处理中抛异常,也要确保 remove() 执行,可用 try-finally 包裹
- select() 返回 0 表示超时或被唤醒,直接 continue 即可,不用清空 keys
读写逻辑要适配非阻塞语义
非阻塞模式下,read() 和 write() 都可能返回 0,不代表错误,只代表当前无数据可读 / 缓冲区暂不可写。真实业务逻辑要围绕“是否还有空间/数据”展开:
- read() 返回 >0:把数据从 Buffer 拷出,再 flip() 准备写入应用缓冲区
- read() 返回 -1:对端关闭连接,应取消注册、关闭 channel
- write() 返回 0:说明 TCP 发送缓冲区已满,可暂时取消 OP_WRITE,等下次 OP_WRITE 就绪再继续
- 务必在每次 read/write 后检查 Buffer 的 position 和 remaining,避免丢数据或无限循环
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










