java nio 通过非阻塞 channel、规范操作 buffer 和 selector 事件驱动三者协同,实现高效不阻塞的数据传输:channel 需显式设为非阻塞,read/write 立即返回实际字节数;buffer 必须按 clear/flip/compact 流程管理状态;selector 负责监听就绪事件,避免轮询,构成完整非阻塞闭环。

Java NIO 通过 Channel 和 Buffer 的协同配合,配合非阻塞模式设置与状态检查机制,实现高效、不卡线程的数据传输。关键不在“能不能传”,而在于“怎么知道什么时候能传、传多少、是否要切换任务”。
Channel 必须设为非阻塞模式
默认情况下 Channel 是阻塞的,必须显式调用 configureBlocking(false) 才能启用非阻塞行为。一旦设置,read() 或 write() 方法会立即返回:
- read() 返回实际读到的字节数(可能为 0),不会等待数据到达;
- write() 返回实际写出的字节数(可能小于 buffer 中剩余数据量),也不会等缓冲区清空;
- 如果底层没有就绪数据或写入空间,操作立刻返回 0,线程可立刻转向其他任务。
Buffer 需按标准流程翻转和清理
Buffer 不是拿来即用的容器,它的 position、limit、capacity 三者关系决定数据能否被正确读写。典型流程如下:
- 写入前:调用 buffer.clear() 或 buffer.compact() 准备写入空间;
- 写入后:调用 buffer.flip() 切换至读模式(position 归 0,limit 设为当前 position);
- 读取后:若需复用 buffer,用 buffer.compact() 保留未读完数据,或 buffer.clear() 全部重置。
漏掉 flip 就读不到刚写进的数据;没 clear/compact 就可能因 position 超限导致后续 write 失败或静默丢数据。
Channel 与 Buffer 的数据搬运靠 read/write 方法驱动
所有 I/O 操作都围绕 channel.read(buffer) 和 channel.write(buffer) 展开,它们不是“自动完成”,而是同步执行一次搬运:
- read(buffer):从通道把数据填入 buffer,最多填到 limit 位置,返回实际字节数;
- write(buffer):把 buffer 中从 position 到 limit 的数据发往通道,返回实际写出字节数;
- 两者都不保证一次性处理全部数据,常需循环调用,直到 buffer 的 hasRemaining() 为 false 或返回值 ≤ 0。
非阻塞真正落地依赖 Selector 协同判断就绪状态
单靠非阻塞 Channel + Buffer 还不够——你得知道“现在能不能读/写”,否则只能轮询浪费 CPU。Selector 就是这个“裁判”:
- Channel 注册到 Selector 时指定兴趣集(如 OP_READ、OP_WRITE);
- 调用 selector.select() 阻塞等待(或用 selectNow() 立即返回),获取就绪的 SelectionKey 集合;
- 遍历 keys,对每个 key 获取关联 channel,再用它操作对应的 buffer —— 此时 read/write 几乎必然成功或至少有进展。
这才是非阻塞 I/O 的完整闭环:不靠线程等,靠事件通知 + 显式状态检查 + buffer 精确控制。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











