我们在学习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异常:

  1. 试图从PipedInputStream读取数据,
  2. PipedInputStream的缓冲区为“空”(即不存在可读取的数据),
  3. 最后一个向PipedOutputStream写数据的线程不再活动(通过Thread.isAlive()检测)。

这是一个很微妙的时刻,同时也是一个极其重要的时刻。假定有一个线程w向PipedOutputStream写入数据;另一个线程r从对应的 PipedInputStream读取数据。下面一系列的事件将导致r线程在试图读取PipedInputStream时遇到IOException异常:

  1. w向PipedOutputStream写入数据。
  2. w结束(w.isAlive()返回false)。
  3. r从PipedInputStream读取w写入的数据,清空PipedInputStream的缓冲区。
  4. r试图再次从PipedInputStream读取数据。这时PipedInputStream的缓冲区已经为空,而且w已经结束,从而导致在读操作执行时出现IOException异常。

如果一个写操作在PipedOutputStream上执行,同时最近从对应PipedInputStream读取的线程已经不再活动(通过 Thread.isAlive()检测),则写操作将抛出一个IOException异常。假定有两个线程w和r,w向 PipedOutputStream写入数据,而r则从对应的PipedInputStream读取。下面一系列的事件将导致w线程在试图写入 PipedOutputStream时遇到IOException异常:

  1. 写操作线程w已经创建,但r线程还不存在。
  2. w向PipedOutputStream写入数据。
  3. 读线程r被创建,并从PipedInputStream读取数据。
  4. r线程结束。
  5. w企图向PipedOutputStream写入数据,发现r已经结束,抛出IOException异常。

此篇文章主要用于理解运用管道流,如果在实际项目开发中用到的话建议一定要研究透在用,他的坑可不止我上面诺列的这些哦

有问题可以在下面评论,技术问题可以私聊我

Java中的管道流 PipedOutputStream和PipedInputStream的更多相关文章

  1. java下管道流 PipedOutputStream 与PipedInputStream

    package cn.stat.p2.demo; import java.io.IOException; import java.io.PipedInputStream; import java.io ...

  2. Java使用PipedStream管道流通信

    多线程使用PipedStream 通讯 Java 提供了四个相关的管道流,我们可以使用其在多线程进行数据传递,其分别是 类名 作用 备注 PipedInputStream 字节管道输入流 字节流 Pi ...

  3. java多线程通过管道流实现不同线程之间的通信

    java中的管道流(pipeStream)是一种特殊的流,用于在不同线程间直接传送数据.一个线程发送数据到输出管道,另外一个线程从输入管道中读取数据.通过使用管道,实现不同线程间的通信,而不必借助类似 ...

  4. java中PipedStream管道流通信详细使用(详解)

    多线程使用PipedStream 通讯 Java 提供了四个相关的管道流,我们可以使用其在多线程进行数据传递,其分别是 类名 作用 备注 PipedInputStream 字节管道输入流 字节流 Pi ...

  5. Java中的IO流(六)

    上一篇<Java中的IO流(五)>把流中的打印流PrintStream,PrintWriter,序列流SequenceInputStream以及结合之前所记录的知识点完成了文件的切割与文件 ...

  6. java中的Stream流

    java中的Stream流 说到Stream便容易想到I/O Stream,而实际上,谁规定"流"就一定是"IO流"呢?在Java 8中,得益于Lambda所带 ...

  7. java中的IO流

    Java中的IO流 在之前的时候我已经接触过C#中的IO流,也就是说集中数据固化的方式之一,那么我们今天来说一下java中的IO流. 首先,我们学习IO流就是要对文件或目录进行一系列的操作,那么怎样操 ...

  8. java中的缓冲流BufferedWriter和BufferedReader

    java中的缓冲流有BufferedWriter和BufferedReader 在java api 手册中这样说缓冲流: 从字符输入流中读取文本,缓冲各个字符,从而实现字符.数组和行的高效读取.可以指 ...

  9. java 中 “文件” 和 “流” 的简单分析

    java 中 FIle 和 流的简单分析 File类 简单File 常用方法 创建一个File 对象,检验文件是否存在,若不存在就创建,然后对File的类的这部分操作进行演示,如文件的名称.大小等 / ...

随机推荐

  1. jmeter录制火狐浏览器

    昨天看youtube,居然发现有这功能,啊哈哈 听不懂英语 也能有收获. 一.Jmeter 文件中选择Templates->Recording 二.创建之后,自动出现一个模板: 1. Threa ...

  2. 洛谷 1894 [USACO4.2]完美的牛栏The Perfect Stall

    [题解] 其实是个二分图最大匹配的模板题,直接上匈牙利算法就好了. #include<cstdio> #include<algorithm> #define N 1010 #d ...

  3. C. Day at the Beach

    codeforces 599c C. Day at the Beach One day Squidward, Spongebob and Patrick decided to go to the be ...

  4. 调试pcb板子的步骤

    在从外边焊回来的板子中查找问题的时候,如果只是简单的 一通乱调,很有可能一下子就调好了,但是大多数的时候是调了半天,不知道接下来该如何进行,因此,严格的按照步骤走,是个不错的想法: 1.拿到板子的第一 ...

  5. Linux下汇编语言学习笔记76 ---

    这是17年暑假学习Linux汇编语言的笔记记录,参考书目为清华大学出版社 Jeff Duntemann著 梁晓辉译<汇编语言基于Linux环境>的书,喜欢看原版书的同学可以看<Ass ...

  6. [bzoj2097][Usaco2010 Dec]Exercise 奶牛健美操_贪心_树形dp_二分

    Exercise bzoj-2097 Usaco-2010 Dec 题目大意:题目链接 注释:略. 想法:题目描述生怕你不知道这题在考二分. 关键是怎么验证?我们想到贪心的删边. 这样的策略是显然正确 ...

  7. windows 2008 64位在指定的 DSN 中,驱动程序和应用程序之间的体系结构不匹配

    在本机32位环境中使用access数据库正常. 公布到server上时提示:在指定的 DSN 中,驱动程序和应用程序之间的体系结构不匹配 server是64位环境.windows 2008,64位的t ...

  8. java界面编程(9) ------ 列表框

    本文是自己学习所做笔记,欢迎转载.但请注明出处:http://blog.csdn.net/jesson20121020 列表框和JComboBox组合框明显不同,这不不过体如今外观上. 当激活JCom ...

  9. HDU 5100 Chessboard 用 k &#215; 1 的矩形覆盖 n &#215; n 的正方形棋盘

    pid=5100">点击打开链接 Chessboard Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32 ...

  10. C# Json反序列化 C# 实现表单的自动化测试<通过程序控制一个网页> 验证码处理类:UnCodebase.cs + BauDuAi 读取验证码的值(并非好的解决方案) 大话设计模式:原型模式 C# 深浅复制 MemberwiseClone

    C# Json反序列化   Json反序列化有两种方式[本人],一种是生成实体的,方便处理大量数据,复杂度稍高,一种是用匿名类写,方便读取数据,较为简单. 使用了Newtonsoft.Json,可以自 ...