Java NIO的三大核心组件是Buffer、Channel和Selector:Buffer作为带状态的内存容器管理数据读写边界,Channel是双向非阻塞的数据传输通道,Selector通过事件驱动实现单线程多路复用。

Java NIO 的非阻塞 IO 模型之所以能支撑高并发、低线程开销的服务,根本在于三个组件各司其职又紧密协同:Buffer 是数据暂存的“中转站”,Channel 是双向流通的“管道”,Selector 是单线程调度多连接的“交通指挥中心”。漏掉任一环,NIO 就退化为普通 IO。
Buffer:不是普通数组,是带状态的内存容器
Buffer 不是简单字节数组,而是一块有明确读写边界和状态切换逻辑的内存区域。它强制要求“先写后读”,且必须通过 flip()、clear() 等方法显式管理 position 和 limit 才能正确流转数据。
- 四个关键属性不可混淆:capacity(固定容量)、position(当前读/写位置)、limit(读/写上限)、mark(可选标记位)
- 模式切换是核心动作:写完调 flip(),把 position 归零、limit 设为实际写入长度;读完调 clear(),重置 position=0、limit=capacity,准备下一轮
- 常见陷阱:忘记 flip() 就直接 read 或 write → 数据“看不见”;重复调用 flip() 或未 clear 就继续写 → 覆盖或越界
Channel:连接数据源与 Buffer 的双向通道
Channel 是真实的数据搬运工,它不持有数据,只负责把数据从外部(文件、Socket)搬进 Buffer,或从 Buffer 搬到外部。所有 Channel 必须设为非阻塞模式,才能配合 Selector 使用。
- 典型实现包括:FileChannel(文件读写)、SocketChannel(TCP 客户端)、ServerSocketChannel(TCP 服务端)、DatagramChannel(UDP)
- 操作必须绑定 Buffer:read(buffer) 表示“从通道读到 buffer”,write(buffer) 表示“从 buffer 写到通道”,不能跳过 Buffer 直接操作
- 注册前必设非阻塞:channel.configureBlocking(false),否则无法注册到 Selector,会抛 IllegalBlockingModeException
Selector:单线程管理成百上千连接的关键
Selector 是 NIO 实现“一个线程管多个连接”的核心。它不主动轮询,而是让内核通知哪些 Channel 准备就绪,再集中处理——这才是真正的事件驱动。
- 注册流程三步走:创建 Selector → Channel 配置非阻塞 → channel.register(selector, interestOps),如 OP_READ | OP_CONNECT
- select() 返回后必须遍历并清理 selectedKeys():每次调用 selector.selectedKeys() 返回的是一个 Set,需手动遍历处理,并对每个 key 调用 key.remove(),否则该 key 会持续出现在下次 select 结果中,引发 CPU 空转
- 一个 Channel 只能注册到一个 Selector,但一个 Selector 可同时管理数千个 Channel;每个就绪事件对应一个 SelectionKey,从中可获取 channel、附件对象及就绪类型
Buffer、Channel、Selector 不是孤立模块,而是一个闭环:数据经 Channel 流入 Buffer,Selector 发现 Channel 就绪,线程便从 Buffer 提取数据处理,处理完再把响应写回 Buffer 经同一 Channel 发出。理解这个闭环,才能真正用好 NIO。

















