java中pipedinputstream与pipedoutputstream可实现两线程单向字节流通信,需成对绑定、避免同线程使用以防死锁;写线程先启动,读线程及时消费,缓冲区满则write阻塞;推荐优先使用blockingqueue或exchanger等高级并发工具。

Java 中通过 PipedInputStream 和 PipedOutputStream 配合,可以实现两个线程之间的单向字节流通信——一个线程写,另一个线程读。关键在于它们必须成对绑定,且不能在同一个线程中使用(否则会死锁)。
管道流必须配对连接才能工作
PipedInputStream 本身不持有数据,它依赖关联的 PipedOutputStream 写入数据;反之亦然。连接方式有两种:
- 构造时直接传入对方实例:
PipedInputStream pis = new PipedInputStream();<br> PipedOutputStream pos = new PipedOutputStream(pis);
- 或调用
connect()方法手动绑定:PipedInputStream pis = new PipedInputStream();<br> PipedOutputStream pos = new PipedOutputStream();<br> pis.connect(pos);
未连接就尝试读/写会抛出 IOException。
写线程要先启动,但读线程需及时消费
管道内部有默认 1024 字节的缓冲区。如果写线程持续写入、读线程迟迟不读,缓冲区满后 write() 会阻塞,直到读线程读走部分数据。因此:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 不要让写线程一次性写入远超缓冲区的数据(如大文件),避免长时间阻塞
- 读线程应在写线程开始后尽快进入
read()循环,保持流动 - 若写完需通知读端结束,应调用
pos.close(),此时pis.read()会返回 -1
典型用法:一个线程生成数据,另一个线程处理
例如模拟日志采集与分析:
- 生产者线程:用
PipedOutputStream写入日志字符串(需转为字节数组) - 消费者线程:用
PipedInputStream逐字节或按缓冲区读取,再转成字符串解析 - 注意字符编码:建议统一用
StandardCharsets.UTF_8编码/解码,避免乱码
示例片段(省略异常处理):
PipedInputStream pis = new PipedInputStream();
PipedOutputStream pos = new PipedOutputStream(pis);
new Thread(() -> {
try (pos) {
pos.write("INFO: User login\n".getBytes(UTF_8));
pos.write("WARN: Retry limit hit\n".getBytes(UTF_8));
} catch (IOException e) { /* ... */ }
}).start();
new Thread(() -> {
try (pis) {
byte[] buf = new byte[1024];
int len;
while ((len = pis.read(buf)) != -1) {
System.out.print(new String(buf, 0, len, UTF_8));
}
} catch (IOException e) { /* ... */ }
}).start();
替代方案更推荐:使用 BlockingQueue 或 Exchanger
虽然管道流能工作,但它操作的是字节流,处理结构化数据(如对象、消息)需要额外序列化,且容易因阻塞逻辑不当引发死锁或资源泄漏。现代 Java 更倾向以下方式:
-
BlockingQueue:线程安全,支持泛型,天然解耦生产/消费节奏(如
LinkedBlockingQueue) - Exchanger:适合两个线程交换数据块,语义清晰
- Phaser / CountDownLatch:配合普通队列做协调,灵活性更高
除非明确需要字节流语义(如模拟 Unix 管道、对接底层 IO),否则优先考虑这些高级并发工具。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










