Java中的管道流 PipedOutputStream和PipedInputStream
我们在学习IO流的时候可能会学字节流、字符流等,但是关于管道流的相信大部分视频或者教程都是一语带过,第一个是因为这个东西在实际开发中用的也不是很多,但是学习无止境,存在既有理。JDK中既然有个类那说明他并不是一无是处,只是我们目前还没有场景用到它,那说明我们说的还不够,知识点还不足以去驾驭它。

管道流其实是一个很有魅力的流,用法也很独特。他用来连接两个线程之间的通信,比如传输文件等。它们的作用是让多线程可以通过管道进行线程间的通讯。在使用管道通信时,必须将PipedOutputStream和PipedInputStream配套使用。费话不多说,我们来看一个例子:
public class PipdTest {
public static void main(String[] args) throws IOException {
// 创建一个发送者对象
Sender sender = new Sender();
// 创建一个接收者对象
Receiver receiver = new Receiver();
// 获取输出管道流
PipedOutputStream outputStream = sender.getOutputStream();
// 获取输入管道流
PipedInputStream inputStream = receiver.getInputStream();
// 链接两个管道,这一步很重要,把输入流和输出流联通起来
outputStream.connect(inputStream);
// 启动发送者线程
sender.start();
// 启动接收者线程
receiver.start();
}
}
/**
* 发送线程
*
* @author yuxuan
*
*/
class Sender extends Thread {
// 声明一个 管道输出流对象 作为发送方
private PipedOutputStream outputStream = new PipedOutputStream();
public PipedOutputStream getOutputStream() {
return outputStream;
}
@Override
public void run() {
String msg = "Hello World";
try {
outputStream.write(msg.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 关闭输出流
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 接收线程
*
* @author yuxuan
*
*/
class Receiver extends Thread {
// 声明一个 管道输入对象 作为接收方
private PipedInputStream inputStream = new PipedInputStream();
public PipedInputStream getInputStream() {
return inputStream;
}
@Override
public void run() {
byte[] buf = new byte[1024];
try {
// 通过read方法 读取长度
int len = inputStream.read(buf);
System.out.println(new String(buf, 0, len));
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 关闭输入流
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
上面的代码有几个点需要掌握清楚。
1、第一个就是connect方法,他的源码是这么写的
public synchronized void connect(PipedInputStream snk) throws IOException {
if (snk == null) {
throw new NullPointerException();
} else if (sink != null || snk.connected) {
throw new IOException("Already connected");
}
sink = snk;
/*代表连接该管道输入流的输出流PipedOutputStream下一个字节将存储在循环缓冲数组buffer的位置。
当in<0说明缓冲数组是空的;当in==out说明缓冲数组已满。*/
snk.in = -1;
//代表该管道输入流下一个要读取的字节在循环缓冲数组中的位置
snk.out = 0;
//表示该管道输入流是否与管道输出流建立了连接,true为已连接
snk.connected = true;
}
我们可以看到,他是一个线程同步的方法,通过synchronized 关键字修饰。
除了调用connect方法之外,还可以在构造函数中直接传进去,源码如下:

当然管道流也有一些注意事项:
- 管道流仅用于多个线程之间传递信息,若用在同一个线程中可能会造成死锁;
- 管道流的输入输出是成对的,一个输出流只能对应一个输入流,使用构造函数或者connect函数进行连接;
- 一对管道流包含一个缓冲区,其默认值为1024个字节,若要改变缓冲区大小,可以使用带有参数的构造函数;
- 管道的读写操作是互相阻塞的,当缓冲区为空时,读操作阻塞;当缓冲区满时,写操作阻塞;
- 管道依附于线程,因此若线程结束,则虽然管道流对象还在,仍然会报错“read dead end”;
- 管道流的读取方法与普通流不同,只有输出流正确close时,输出流才能读到-1值。
下面我们来看write方法的源码:

看到这里是不是一目了然了。以下还有一些注意事项,我们来看:
PipedInputStream运用的是一个1024字节固定大小的循环缓冲区。写入PipedOutputStream的数据实际上保存到对应的 PipedInputStream的内部缓冲区。从PipedInputStream执行读操作时,读取的数据实际上来自这个内部缓冲区。如果对应的 PipedInputStream输入缓冲区已满,任何企图写入PipedOutputStream的线程都将被阻塞。而且这个写操作线程将一直阻塞,直至出现读取PipedInputStream的操作从缓冲区删除数据。
这意味着,向PipedOutputStream写数据的线程不应该是负责从对应PipedInputStream读取数据的唯一线程。从图二可以清楚地看出这里的问题所在:假设线程t是负责从PipedInputStream读取数据的唯一线程;另外,假定t企图在一次对 PipedOutputStream的write()方法的调用中向对应的PipedOutputStream写入2000字节的数据。在t线程阻塞之前,它最多能够写入1024字节的数据(PipedInputStream内部缓冲区的大小)。然而,一旦t被阻塞,读取 PipedInputStream的操作就再也不会出现,因为t是唯一读取PipedInputStream的线程。这样,t线程已经完全被阻塞,同时,所有其他试图向PipedOutputStream写入数据的线程也将遇到同样的情形。这并不意味着在一次write()调用中不能写入多于1024字节的数据。但应当保证,在写入数据的同时,有另一个线程从PipedInputStream读取数据。
从PipedInputStream读取数据时,如果符合下面三个条件,就会出现IOException异常:
- 试图从PipedInputStream读取数据,
- PipedInputStream的缓冲区为“空”(即不存在可读取的数据),
- 最后一个向PipedOutputStream写数据的线程不再活动(通过Thread.isAlive()检测)。
这是一个很微妙的时刻,同时也是一个极其重要的时刻。假定有一个线程w向PipedOutputStream写入数据;另一个线程r从对应的 PipedInputStream读取数据。下面一系列的事件将导致r线程在试图读取PipedInputStream时遇到IOException异常:
- w向PipedOutputStream写入数据。
- w结束(w.isAlive()返回false)。
- r从PipedInputStream读取w写入的数据,清空PipedInputStream的缓冲区。
- r试图再次从PipedInputStream读取数据。这时PipedInputStream的缓冲区已经为空,而且w已经结束,从而导致在读操作执行时出现IOException异常。
如果一个写操作在PipedOutputStream上执行,同时最近从对应PipedInputStream读取的线程已经不再活动(通过 Thread.isAlive()检测),则写操作将抛出一个IOException异常。假定有两个线程w和r,w向 PipedOutputStream写入数据,而r则从对应的PipedInputStream读取。下面一系列的事件将导致w线程在试图写入 PipedOutputStream时遇到IOException异常:
- 写操作线程w已经创建,但r线程还不存在。
- w向PipedOutputStream写入数据。
- 读线程r被创建,并从PipedInputStream读取数据。
- r线程结束。
- w企图向PipedOutputStream写入数据,发现r已经结束,抛出IOException异常。
此篇文章主要用于理解运用管道流,如果在实际项目开发中用到的话建议一定要研究透在用,他的坑可不止我上面诺列的这些哦
有问题可以在下面评论,技术问题可以私聊我
Java中的管道流 PipedOutputStream和PipedInputStream的更多相关文章
- java下管道流 PipedOutputStream 与PipedInputStream
package cn.stat.p2.demo; import java.io.IOException; import java.io.PipedInputStream; import java.io ...
- Java使用PipedStream管道流通信
多线程使用PipedStream 通讯 Java 提供了四个相关的管道流,我们可以使用其在多线程进行数据传递,其分别是 类名 作用 备注 PipedInputStream 字节管道输入流 字节流 Pi ...
- java多线程通过管道流实现不同线程之间的通信
java中的管道流(pipeStream)是一种特殊的流,用于在不同线程间直接传送数据.一个线程发送数据到输出管道,另外一个线程从输入管道中读取数据.通过使用管道,实现不同线程间的通信,而不必借助类似 ...
- java中PipedStream管道流通信详细使用(详解)
多线程使用PipedStream 通讯 Java 提供了四个相关的管道流,我们可以使用其在多线程进行数据传递,其分别是 类名 作用 备注 PipedInputStream 字节管道输入流 字节流 Pi ...
- Java中的IO流(六)
上一篇<Java中的IO流(五)>把流中的打印流PrintStream,PrintWriter,序列流SequenceInputStream以及结合之前所记录的知识点完成了文件的切割与文件 ...
- java中的Stream流
java中的Stream流 说到Stream便容易想到I/O Stream,而实际上,谁规定"流"就一定是"IO流"呢?在Java 8中,得益于Lambda所带 ...
- java中的IO流
Java中的IO流 在之前的时候我已经接触过C#中的IO流,也就是说集中数据固化的方式之一,那么我们今天来说一下java中的IO流. 首先,我们学习IO流就是要对文件或目录进行一系列的操作,那么怎样操 ...
- java中的缓冲流BufferedWriter和BufferedReader
java中的缓冲流有BufferedWriter和BufferedReader 在java api 手册中这样说缓冲流: 从字符输入流中读取文本,缓冲各个字符,从而实现字符.数组和行的高效读取.可以指 ...
- java 中 “文件” 和 “流” 的简单分析
java 中 FIle 和 流的简单分析 File类 简单File 常用方法 创建一个File 对象,检验文件是否存在,若不存在就创建,然后对File的类的这部分操作进行演示,如文件的名称.大小等 / ...
随机推荐
- jmeter录制火狐浏览器
昨天看youtube,居然发现有这功能,啊哈哈 听不懂英语 也能有收获. 一.Jmeter 文件中选择Templates->Recording 二.创建之后,自动出现一个模板: 1. Threa ...
- 洛谷 1894 [USACO4.2]完美的牛栏The Perfect Stall
[题解] 其实是个二分图最大匹配的模板题,直接上匈牙利算法就好了. #include<cstdio> #include<algorithm> #define N 1010 #d ...
- C. Day at the Beach
codeforces 599c C. Day at the Beach One day Squidward, Spongebob and Patrick decided to go to the be ...
- 调试pcb板子的步骤
在从外边焊回来的板子中查找问题的时候,如果只是简单的 一通乱调,很有可能一下子就调好了,但是大多数的时候是调了半天,不知道接下来该如何进行,因此,严格的按照步骤走,是个不错的想法: 1.拿到板子的第一 ...
- Linux下汇编语言学习笔记76 ---
这是17年暑假学习Linux汇编语言的笔记记录,参考书目为清华大学出版社 Jeff Duntemann著 梁晓辉译<汇编语言基于Linux环境>的书,喜欢看原版书的同学可以看<Ass ...
- [bzoj2097][Usaco2010 Dec]Exercise 奶牛健美操_贪心_树形dp_二分
Exercise bzoj-2097 Usaco-2010 Dec 题目大意:题目链接 注释:略. 想法:题目描述生怕你不知道这题在考二分. 关键是怎么验证?我们想到贪心的删边. 这样的策略是显然正确 ...
- windows 2008 64位在指定的 DSN 中,驱动程序和应用程序之间的体系结构不匹配
在本机32位环境中使用access数据库正常. 公布到server上时提示:在指定的 DSN 中,驱动程序和应用程序之间的体系结构不匹配 server是64位环境.windows 2008,64位的t ...
- java界面编程(9) ------ 列表框
本文是自己学习所做笔记,欢迎转载.但请注明出处:http://blog.csdn.net/jesson20121020 列表框和JComboBox组合框明显不同,这不不过体如今外观上. 当激活JCom ...
- HDU 5100 Chessboard 用 k × 1 的矩形覆盖 n × n 的正方形棋盘
pid=5100">点击打开链接 Chessboard Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32 ...
- C# Json反序列化 C# 实现表单的自动化测试<通过程序控制一个网页> 验证码处理类:UnCodebase.cs + BauDuAi 读取验证码的值(并非好的解决方案) 大话设计模式:原型模式 C# 深浅复制 MemberwiseClone
C# Json反序列化 Json反序列化有两种方式[本人],一种是生成实体的,方便处理大量数据,复杂度稍高,一种是用匿名类写,方便读取数据,较为简单. 使用了Newtonsoft.Json,可以自 ...