Java NIO Pipe是同一JVM内两线程单向低开销通信机制,基于内核环形缓冲区,需严格遵循初始化、非阻塞I/O、缓冲区状态管理及协议化终止规则。

Java NIO 的 Pipe 是专为同一 JVM 内两个线程间单向、低开销通信设计的原生机制,不依赖锁、不序列化、零拷贝,底层复用操作系统内核的环形缓冲区(默认约 8 KiB)。它适合生产者-消费者模型,但必须严格遵循初始化、状态管理和生命周期协同规则。
创建并绑定一对强关联通道
Pipe 必须一次性创建,生成不可拆分的一对通道:
- 调用 Pipe.open() 原子性获得完整管道实例
- Pipe.SinkChannel 是唯一写入端,只支持 write(),不可读
- Pipe.SourceChannel 是唯一读取端,只支持 read(),不可写
- 两端共享同一内核缓冲区资源,任意一端 close() 后,另一端再 I/O 就抛 ClosedChannelException
写线程:非阻塞写入 + 显式缓冲区控制
阻塞模式下容易挂起线程,必须设为非阻塞:
- 调用 sinkChannel.configureBlocking(false)
- 每次写前确保 ByteBuffer 处于写模式,put() 后必须 flip()(position=0,limit=有效字节数)
- write(buffer) 返回值决定后续动作:
→ 返回 0:缓冲区满,建议 Thread.yield() 或交由调度器延后重试
→ 返回正值但小于 buffer.remaining():说明未全写入,需 buffer.compact() 整理残留数据再继续
→ 写完可调用 buffer.clear() 复用 - 常见错误:忘记 flip() 导致 write() 实际写入 0 字节;关闭 sink 后仍尝试写入
读线程:安全读取 + 正确解析字节边界
SourceChannel 支持注册到 Selector(SinkChannel 不支持),推荐结合非阻塞使用:
立即学习“Java免费学习笔记(深入)”;
- 调用 sourceChannel.configureBlocking(false)
- 注册时用 sourceChannel.register(selector, OP_READ)
- read(buffer) 返回值含义明确:
→ -1:写入端已关闭,应终止循环
→ 0:当前无数据(正常现象,非错误)
→ 正值:实际读取字节数,之后必须 buffer.flip() 切换至读模式才能正确提取数据 - 处理完数据后调用 buffer.clear(),否则下次 read() 可能因 position/limit 错乱而失败
- 常见错误:把返回 0 当作结束信号导致死循环;读取后未 flip() 就直接 array(),结果为空或乱码
协作终止与消息边界处理
Pipe 本身不提供关闭通知或消息分界能力,需双方约定协议:
- 不推荐用“写入空 buffer”作为结束标志(read() 返回 0 表示暂无数据,不是流结束)
- 可行方案包括:
→ 发送固定长度头 + 实际内容(如前 4 字节表示后续 payload 长度)
→ 写入特定终止标记,例如 UTF-8 编码的 "END\0" 字节序列
→ 由写线程主动 close() sinkChannel,读线程检测 read() 返回 -1 后退出 - 注意:close() 是最终动作,不可在仍有数据未读完时提前调用


















