如何在 Java 中使用 PipedOutputStream 实现线程间字节数据的单向异步传输

陌枫酱_1443

陌枫酱_1443

2026-04-28

226人浏览

原创

PipedOutputStream不能单独工作,因其必须与PipedInputStream成对绑定,否则写入时抛出“Pipe not connected”异常;它不持缓冲区,数据直推输入流,且连接须在写操作前完成。

如何在 java 中使用 pipedoutputstream 实现线程间字节数据的单向异步传输

为什么 PipedOutputStream 不能单独工作

PipedOutputStream 必须与 PipedInputStream 成对绑定,否则写入时会立即阻塞或抛出 IOException: Pipe not connected。它本身不持有缓冲区,数据直接推给配对的输入流——这意味着你不能“先写再连”,必须在写线程启动前完成连接。

常见错误是分别 new 两个管道流后直接 start 线程,却忘了调用 connect() 或依赖构造函数自动连接。推荐始终显式调用 connect(),避免 JDK 版本差异导致的隐式行为不一致:

PipedInputStream pis = new PipedInputStream();
PipedOutputStream pos = new PipedOutputStream();
try {
    pos.connect(pis); // 必须调用,不要依赖 PipedOutputStream(PIpedInputStream) 构造器
} catch (IOException e) {
    throw new RuntimeException(e);
}

写入线程卡住?检查读取端是否及时消费

PipedOutputStream 的 write 操作是同步且阻塞的:当配对的 PipedInputStream 内部缓冲区(默认 1024 字节)满时,write() 会一直等待,直到读线程调用 read() 腾出空间。这不是 bug,而是设计使然——它天然实现背压(backpressure)。

容易踩的坑:

  • 读线程未启动,或启动晚于写线程,导致写入方永久阻塞
  • 读线程只调用一次 read() 就退出,缓冲区无法释放
  • 读取时使用了带超时的 read(byte[], int, int) 但未处理返回值为 -1(流关闭)或 0(无数据),误判为异常

安全做法是让读线程循环调用 read(),并在捕获 IOException(如管道关闭)或返回 -1 时退出:

new Thread(() -> {
    byte[] buf = new byte[1024];
    try {
        int n;
        while ((n = pis.read(buf)) != -1) {
            // 处理 buf[0..n)
        }
    } catch (IOException e) {
        // 管道已断开或出错,正常结束
    }
}).start();

关闭顺序很重要:先关输出端,再关输入端

调用 pos.close() 会向管道发送 EOF 信号,触发 pis.read() 返回 -1;若反过来先关 pis,再写入 pos,会立即抛出 IOException: Write end dead。

Java注释规范Skills
Java注释规范Skills

当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。

下载

正确关闭逻辑:

  • 写线程完成数据写入后,调用 pos.close()
  • 读线程在 read() 返回 -1 后自然退出,随后可安全调用 pis.close()(虽然通常非必需)
  • 不要在读线程中主动调用 pis.close() 来“中断”写线程——这会导致写入方异常,且无法保证数据完整性

注意:PipedInputStream 的 available() 返回的是当前缓冲区字节数,不是“是否还有数据”,不能用来轮询判断 EOF。

替代方案比 PipedOutputStream 更可靠吗

纯内存管道在高吞吐或大数据量场景下容易成为瓶颈:缓冲区小、无超时控制、异常传播不直观。实际项目中更推荐以下替代:

  • 小数据 + 控制流:BlockingQueue 或 ArrayBlockingQueue,支持容量限制和 offer/poll 超时
  • 需要流式处理大文件:ByteArrayInputStream / ByteArrayOutputStream 配合 ExecutorService 提交任务,避免线程耦合
  • 跨 JVM 或需持久化:Files.newByteChannel() + 临时文件,或内存映射文件 MappedByteBuffer

PipedOutputStream 的价值仅限于极简 demo、教学演示或遗留代码兼容。真实异步传输务必考虑中断、超时、重试和资源泄漏——这些它都不提供。

最常被忽略的一点:JDK 文档明确指出,管道流不是为多写一读或多读一写设计的。哪怕只是两个写线程同时往同一个 PipedOutputStream 写,也会因竞争导致数据交错或 IOException,这点几乎没人测试就上线。

Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

2023.06.15

11397

6

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

2023.07.05

8502

9

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

2023.07.31

7332

8

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.01

1224

3

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.02

988

3

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

1456

5

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

2989

5

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

2023.08.03

20251

3

配置java环境变量
配置java环境变量

配置Java环境变量是为了让操作系统能够识别和使用Java的相关命令和功能。本专题为大家提供配置java环境变量相关文章,帮助大家解决问题。

2023.08.03

1315

8

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习