java nio的核心是channel、buffer、selector协同机制:数据经channel进出,全程存于buffer,selector单线程调度就绪事件;buffer为强制中介,需flip/clear管理状态;channel为双向管道,不处理缓冲与解析;selector仅轮询就绪事件,不操作数据。

Java NIO 的核心不是“三个独立工具”,而是围绕数据流动路径构建的一套协同机制:数据必须经由 Channel 进出,全程存放在 Buffer 中,而 Selector 负责在单线程内高效调度多个 Channel 的就绪事件。三者缺一不可,且职责边界清晰。
Buffer 是数据的唯一中转站
所有读写操作都必须通过 Buffer —— 它不是临时容器,而是强制中介。Channel 不能直接与应用代码交换字节,必须先写入 Buffer(写模式),再从 Buffer 提取(读模式)。这种设计带来两个关键约束:
- Buffer 有明确状态:capacity 固定不变,position 标记当前读/写位置,limit 划定有效边界;写完必须调用
flip()才能切换为读模式,否则读不到刚写的数据 - 常见误用是忘记 flip 或 clear:写入 10 字节后 position=10,若不 flip 就直接 get(),会从 position=10 开始读,结果为空;读完若不 clear 或 compact,下次写入会覆盖或越界
Channel 是双向数据管道
Channel 不是连接本身,而是操作系统底层 I/O 资源(如 socket、file)的 Java 封装。它只做两件事:把数据从外部传到 Buffer(read),或把 Buffer 数据发到外部(write)。它不管理缓冲、不解析内容、不决定何时读写 —— 这些全由 Buffer 和 Selector 协同控制。
- TCP 场景下,SocketChannel 对应一个客户端连接,ServerSocketChannel 负责接收新连接并生成新的 SocketChannel
- Channel 可注册到 Selector,并声明关注的事件类型(如 OP_READ 表示“数据已到达,可安全 read”);未注册的 Channel 无法被 Selector 管理,也就无法参与非阻塞多路复用
Selector 是单线程的事件调度中枢
Selector 不处理数据,也不操作 Buffer,它的唯一任务是轮询所有注册的 Channel,发现哪些发生了预设事件(如可读、可写、已连接),然后通知应用线程去处理。它让一个线程同时管理成百上千个 Channel 成为可能。
- 调用
select()会阻塞,直到至少一个 Channel 就绪;selectNow()立即返回,适合需完全非阻塞的场景 - 每次 select 返回后,必须遍历
selectedKeys()集合,对每个就绪 Channel 执行对应操作(如可读则分配 ByteBuffer 并 read()),处理完要手动 remove 该 key,否则下次 select 会重复返回 - Selector 本身不保证线程安全,通常绑定在一个专用 I/O 线程中,避免多线程并发注册或调用 select
一次典型 TCP 读操作的协同流程
以服务端接收客户端请求为例:
- ServerSocketChannel 接收新连接,生成 SocketChannel,并将其注册到 Selector,关注 OP_READ 事件
- 客户端发送数据,OS 内核将数据放入 socket 接收缓冲区;Selector 检测到该 Channel 可读,select() 返回
- 程序从 selectedKeys() 中取出该 SocketChannel,分配或复用 ByteBuffer,调用 channel.read(buffer) —— 数据从内核拷贝进 Buffer
- 调用 buffer.flip() 切换为读模式,解析 buffer 中的字节;处理完成后调用 buffer.clear() 重置状态,准备下一次读
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











