PipedInputStream和PipedOutputStream详解
PipedInputStream类与PipedOutputStream类用于在应用程序中创建管道通信.一个PipedInputStream实例对象必须和一个PipedOutputStream实例对象进行连接而产生一个通信管道.PipedOutputStream可以向管道中写入数据,PipedIntputStream可以读取PipedOutputStream向管道中写入的数据.这两个类主要用来完成线程之间的通信.一个线程的PipedInputStream对象能够从另外一个线程的PipedOutputStream对象中读取数据.如下图所示。
PipedInputStream和PipedOutputStream的实现原理类似于"生产者-消费者"原理,PipedOutputStream是生产者,PipedInputStream是消费者,在PipedInputStream中有一个buffer字节数组,默认大小为1024,作为缓冲区,存放"生产者"生产出来的东西.还有两个变量in和out。in是用来记录"生产者"生产了多少,out是用来记录"消费者"消费了多少,in为-1表示消费完了,in==out表示生产满了.当消费者没东西可消费的时候,也就是当in为-1的时候,消费者会一直等待,直到有东西可消费.
1.创建连接与初始化
在两者的构造函数中,都相互提供了连接的构造方法,分别用于接收对方的管道实例,然后调用各自的connect()方法进行连接,如PipedInputStream:
private static final int DEFAULT_PIPE_SIZE = 1024;
protected static final int PIPE_SIZE = DEFAULT_PIPE_SIZE;
// 省略部分构造函数
public PipedInputStream(PipedOutputStream src, int pipeSize) throws IOException {
initPipe(pipeSize);
connect(src);
}
private void initPipe(int pipeSize) { // 初始化buffer的大小,pipeSize可以通过构造方法指定,也可以使用默认的PIPE_SIZE的大小
if (pipeSize <= 0) {
throw new IllegalArgumentException("Pipe Size <= 0");
}
buffer = new byte[pipeSize]; // 初始化缓冲区的大小
}
public void connect(PipedOutputStream src) throws IOException {
src.connect(this); // 连接输入管道
}
看一下PipedOutputStream的构造函数,如下:
private PipedInputStream sink;
public PipedOutputStream(PipedInputStream snk) throws IOException {
connect(snk); // 连接输出管理
}
public PipedOutputStream() { } 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;
snk.in = -1; // buffer数组中无数据
snk.out = 0; // 取出的数据为0
snk.connected = true;// 表示连接成功
}
可以看到,相互之间会调用connect()方法来连接,其效果是一样的。
2.数据的写入与读取
连接成功就,就可以进行数据的写入与读出操作了,在PipedOutputStream中的write()写法如下:
public void write(int b) throws IOException {
if (sink == null) {
throw new IOException("Pipe not connected");
}
sink.receive(b); // 调用receive方法进行数据的输出
}
public void write(byte b[], int off, int len) throws IOException {
if (sink == null) {
throw new IOException("Pipe not connected");
} else if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
sink.receive(b, off, len);
}
方法在写入到byte[]数组缓存区数据后,就会调用PipedInputStream中的receive方法。输出管道中的receive()方法如下:
protected synchronized void receive(int b) throws IOException { // 只会在PipedOutputStream类的write()中调用
checkStateForReceive();
writeSide = Thread.currentThread();
if (in == out) // in==out表示buffer数组已满
awaitSpace(); // 等待空闲空间
if (in < 0) { // 输入管道无数据
in = 0;
out = 0;
}
buffer[in++] = (byte)(b & 0xFF);
if (in >= buffer.length) {
in = 0; // 缓冲区已经满了,等待下一次从头写入
}
} synchronized void receive(byte b[], int off, int len) throws IOException { // 将下标off开始的len个数组数据写入到输出管道中
checkStateForReceive(); // 检查管道的状态
writeSide = Thread.currentThread(); // 获取写入线程
int bytesToTransfer = len;
while (bytesToTransfer > 0) {
if (in == out) // 缓冲数组已满,只能等待
awaitSpace();
int nextTransferAmount = 0;
if (out < in) {
nextTransferAmount = buffer.length - in;
} else if (in < out) {
if (in == -1) {
in = out = 0;
nextTransferAmount = buffer.length - in;
} else {
nextTransferAmount = out - in;
}
}
if (nextTransferAmount > bytesToTransfer)
nextTransferAmount = bytesToTransfer;
assert(nextTransferAmount > 0); // nextTransferAmount<=0,则终止程序的执行
System.arraycopy(b, off, buffer, in, nextTransferAmount);// 拷贝数组中数据到缓冲区
bytesToTransfer -= nextTransferAmount;
off += nextTransferAmount;
in += nextTransferAmount;
if (in >= buffer.length) {
in = 0;
}
}
}
输入管理通过如上的对应方法接收到数据并保存到输入缓冲区后,下面就可以使用read()方法读出这些数据了。看一下awaitSpace()方法的实现源代码:
/*
* 若写入管道的数据正好全部被读取完(例如,管道缓冲满),则执行awaitSpace()操作;
* 让读取管道的线程管道产生读取数据请求,从而才能继续的向“管道”中写入数据
*/
private void awaitSpace() throws IOException {
/*
* 如果管道中被读取的数据,等于写入管道的数据时,
* 则每隔1000ms检查“管道状态”,并唤醒管道操作:若有读取管道数据线程被阻塞,则唤醒该线程
*/
while (in == out) {
checkStateForReceive();
/* full: kick any waiting readers */
notifyAll();
try {
wait(1000);
} catch (InterruptedException ex) {
throw new java.io.InterruptedIOException();
}
}
}
PipedInputStream类中的read()方法源代码如下:
public synchronized int read() throws IOException {
if (!connected) {
throw new IOException("Pipe not connected");
} else if (closedByReader) {
throw new IOException("Pipe closed");
} else if (writeSide != null && !writeSide.isAlive() && !closedByWriter && (in < 0)) {
throw new IOException("Write end dead");
} readSide = Thread.currentThread(); // 获取读取线程
int trials = 2;
while (in < 0) {
if (closedByWriter) { // 如果in<0(表示管道中无数据)且closedByWriter为true(表示输入管道已经关闭)则直接返回-1
return -1;
}
if ((writeSide != null) && (!writeSide.isAlive()) && (--trials < 0)) {
throw new IOException("Pipe broken");
}
notifyAll();
try {
wait(1000);
} catch (InterruptedException ex) {
throw new java.io.InterruptedIOException();
}
}
int ret = buffer[out++] & 0xFF;
if (out >= buffer.length) {
out = 0;
}
if (in == out) {
/* now empty */
in = -1;
}
return ret;
} public synchronized int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
/* possibly wait on the first character */
int c = read();
if (c < 0) {
return -1;
}
b[off] = (byte) c;
int rlen = 1;
while ((in >= 0) && (len > 1)) {
int available;
if (in > out) {
available = Math.min((buffer.length - out), (in - out));
} else {
available = buffer.length - out;
}
// A byte is read beforehand outside the loop
if (available > (len - 1)) {
available = len - 1;
}
System.arraycopy(buffer, out, b, off + rlen, available);
out += available;
rlen += available;
len -= available; if (out >= buffer.length) {
out = 0;
}
if (in == out) {
in = -1;
}
}
return rlen;
}
3.刷新与关闭管道
来看一下管道输出流PipedOutputStream中的刷新和关闭方法,源代码如下:
// 刷回管道输出流
public synchronized void flush() throws IOException {
if (sink != null) {
synchronized (sink) {
/*
* 调用管道输入流的notifyAll(),通知管道输入流放弃对当前资源的占有,
* 让其它的等待线程(等待读取管道输出流的线程)读取管道输出流的值。
*/
sink.notifyAll();
}
}
}
// 关闭管道输出流
public void close() throws IOException {
if (sink != null) {
sink.receivedLast();// 通知管道输入流,输出管理已经关闭
}
}
看一下receivedLast()方法PipedInputStream,如下:
synchronized void receivedLast() {
closedByWriter = true; // 输出管道标志为true,表示关闭
notifyAll(); // 唤醒所有的等待线程
}
通知所有的等待线程,最后的数据已经全部到达。
public void close() throws IOException { // 关闭管道输出流
closedByReader = true;
synchronized (this) {
in = -1; // 清空缓冲区数据
}
}
PipedInputStream源码完整:
package IO;
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedOutputStream; public class PipedInputStream extends InputStream {
boolean closedByWriter = false; // 标识有读取方或写入方关闭
volatile boolean closedByReader = false;
boolean connected = false; // 是否建立连接
Thread readSide; // 标识哪个线程
Thread writeSide; protected static final int PIPE_SIZE = 1024; // 缓冲区的默认大小
protected byte buffer[] = new byte[PIPE_SIZE]; // 缓冲区
protected int in = -1; // 下一个写入字节的位置。0代表空,in==out代表满
protected int out = 0; // 下一个读取字节的位置 public PipedInputStream(PipedOutputStream src) throws IOException { // 给定源的输入流
connect(src);
} public PipedInputStream() {
} // 默认构造器,下部一定要connect源 public void connect(PipedOutputStream src) throws IOException { // 连接输入源
src.connect(this); // 调用源的connect方法连接当前对象
} protected synchronized void receive(int b) throws IOException { // 只被PipedOuputStream调用
checkStateForReceive(); // 检查状态,写入
writeSide = Thread.currentThread(); // 永远是PipedOuputStream
if (in == out)
awaitSpace(); // 输入和输出相等,等待空间
if (in < 0) {
in = 0;
out = 0;
} buffer[in++] = (byte) (b & 0xFF); // 放入buffer相应的位置
if (in >= buffer.length) {
in = 0;
} // in为0表示buffer已空
} synchronized void receive(byte b[], int off, int len) throws IOException {
checkStateForReceive();
writeSide = Thread.currentThread(); // 从PipedOutputStream可以看出
int bytesToTransfer = len;
while (bytesToTransfer > 0) {
if (in == out)
awaitSpace(); // 满了,会通知读取的;空会通知写入
int nextTransferAmount = 0;
if (out < in) {
nextTransferAmount = buffer.length - in;
} else if (in < out) {
if (in == -1) {
in = out = 0;
nextTransferAmount = buffer.length - in;
} else {
nextTransferAmount = out - in;
}
} if (nextTransferAmount > bytesToTransfer)
nextTransferAmount = bytesToTransfer;
assert (nextTransferAmount > 0);
System.arraycopy(b, off, buffer, in, nextTransferAmount);
bytesToTransfer -= nextTransferAmount;
off += nextTransferAmount;
in += nextTransferAmount;
if (in >= buffer.length) {
in = 0;
}
}
} private void checkStateForReceive() throws IOException { // 检查当前状态,等待输入
if (!connected) {
throw new IOException("Pipe not connected");
} else if (closedByWriter || closedByReader) {
throw new IOException("Pipe closed");
} else if (readSide != null && !readSide.isAlive()) {
throw new IOException("Read end dead");
}
} private void awaitSpace() throws IOException { // Buffer已满,等待一段时间
while (in == out) { // in==out表示满了,没有空间
checkStateForReceive(); // 检查接受端的状态
notifyAll(); // 通知读取端
try {
wait(1000);
} catch (InterruptedException ex) {
throw new java.io.InterruptedIOException();
}
}
} synchronized void receivedLast() { // 通知所有等待的线程()已经接受到最后的字节
closedByWriter = true; //
notifyAll();
} public synchronized int read() throws IOException {
if (!connected) { // 检查一些内部状态
throw new IOException("Pipe not connected");
} else if (closedByReader) {
throw new IOException("Pipe closed");
} else if (writeSide != null && !writeSide.isAlive() && !closedByWriter
&& (in < 0)) {
throw new IOException("Write end dead");
}
readSide = Thread.currentThread(); // 当前线程读取
int trials = 2; // 重复两次????
while (in < 0) {
if (closedByWriter) {
return -1;
} // 输入断关闭返回-1
if ((writeSide != null) && (!writeSide.isAlive()) && (--trials < 0)) { // 状态错误
throw new IOException("Pipe broken");
}
notifyAll(); // 空了,通知写入端可以写入
try {
wait(1000);
} catch (InterruptedException ex) {
throw new java.io.InterruptedIOException();
}
}
int ret = buffer[out++] & 0xFF; //
if (out >= buffer.length) {
out = 0;
}
if (in == out) {
in = -1;
} // 没有任何字节
return ret;
} public synchronized int read(byte b[], int off, int len) throws IOException {
if (b == null) { // 检查输入参数的正确性
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read(); // 读取下一个
if (c < 0) {
return -1;
} // 已经到达末尾了,返回-1
b[off] = (byte) c; // 放入外部buffer中
int rlen = 1; // return-len
while ((in >= 0) && (--len > 0)) { // 下一个in存在,且没有到达len
b[off + rlen] = buffer[out++]; // 依次放入外部buffer
rlen++;
if (out >= buffer.length) {
out = 0;
} // 读到buffer的末尾,返回头部
if (in == out) {
in = -1;
} // 读、写位置一致时,表示没有数据
}
return rlen; // 返回填充的长度
} public synchronized int available() throws IOException { // 返回还有多少字节可以读取
if (in < 0)
return 0; // 到达末端,没有字节
else if (in == out)
return buffer.length; // 写入的和读出的一致,表示满
else if (in > out)
return in - out; // 写入的大于读出
else
return in + buffer.length - out; // 写入的小于读出的
} public void close() throws IOException { // 关闭当前流,同时释放与其相关的资源
closedByReader = true; // 表示由输入流关闭
synchronized (this) {
in = -1;
} // 同步化当前对象,in为-1
}
}
PipedOutputStream源码完整:
package IO; import java.io.OutputStream; public class PipedOutputStream extends OutputStream {
private PipedInputStream sink; // 包含一个PipedInputStream public PipedOutputStream(PipedInputStream snk) throws IOException { // 带有目的地的构造器
connect(snk);
} public PipedOutputStream() {
} // 默认构造器,必须使用下面的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; // 一系列初始化工作
snk.in = -1;
snk.out = 0;
snk.connected = true;
} public void write(int b) throws IOException { // 向流中写入数据
if (sink == null) {
throw new IOException("Pipe not connected");
}
sink.receive(b); // 本质上是,调用PipedInputStream的receive方法接受此字节
} public void write(byte b[], int off, int len) throws IOException {
if (sink == null) { // 首先检查输入参数的正确性
throw new IOException("Pipe not connected");
} else if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0)
|| ((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
sink.receive(b, off, len); // 调用PipedInputStream的receive方法接受
} public synchronized void flush() throws IOException { // flush输出流
if (sink != null) {
synchronized (sink) {
sink.notifyAll();
} // 本质是通知输入流,可以读取
}
} public void close() throws IOException { // 关闭流同时释放相关资源
if (sink != null) {
sink.receivedLast();
}
}
}
下面来具体举一个例子,如下:
public class test04 {
public static void main(String [] args) {
Sender sender = new Sender();
Receiver receiver = new Receiver(); PipedOutputStream outStream = sender.getOutStream();
PipedInputStream inStream = receiver.getInStream();
try {
//inStream.connect(outStream); // 与下一句一样
outStream.connect(inStream);
} catch (Exception e) {
e.printStackTrace();
}
sender.start();
receiver.start();
}
} class Sender extends Thread {
private PipedOutputStream outStream = new PipedOutputStream();
public PipedOutputStream getOutStream() {
return outStream;
}
public void run() {
String info = "hello, receiver";
try {
outStream.write(info.getBytes());
outStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
} class Receiver extends Thread {
private PipedInputStream inStream = new PipedInputStream();
public PipedInputStream getInStream() {
return inStream;
}
public void run() {
byte[] buf = new byte[1024];
try {
int len = inStream.read(buf);
System.out.println("receive message from sender : " + new String(buf, 0, len));
inStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
最后运行后输出的结果如下:receive message from sender : hello, receiver
PipedInputStream和PipedOutputStream详解的更多相关文章
- java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET
java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET 亲,“社区之星”已经一周岁了! 社区福利快来领取免费参加MDCC大会机会哦 Tag功能介绍—我们 ...
- JAVA IO 类库详解
JAVA IO类库详解 一.InputStream类 1.表示字节输入流的所有类的超类,是一个抽象类. 2.类的方法 方法 参数 功能详述 InputStream 构造方法 available 如果用 ...
- 面试必备:详解Java I/O流,掌握这些就可以说精通了?
@TOC Java IO概述 IO就是输入/输出.Java IO类库基于抽象基础类InputStream和OutputStream构建了一套I/O体系,主要解决从数据源读入数据和将数据写入到目的地问题 ...
- Linq之旅:Linq入门详解(Linq to Objects)
示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...
- 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)
一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...
- EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解
前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...
- Java 字符串格式化详解
Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...
- Android Notification 详解(一)——基本操作
Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...
- Android Notification 详解——基本操作
Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...
随机推荐
- 区分 点操作符+属性名 和 getAttribute()
在用DOM操作控制HTML时,很多初学者会把 点操作符+属性名 与getAttribute("属性名") 混淆,误以为这两种方法是等价的. 实际上, 通过getAttribute( ...
- 2017-3-28 JavaScript 基础、语法
前端三剑客: html+css+js(html 决定网页上有什么,css决定东西是怎么摆放的,js决定东西的功能) js定义: js是一个脚本语言,需要有宿主文件,它的宿主文件是html文件. js ...
- API的文档自动生成——基于CDIF的SOA基本能力
当前,作为大部分移动app和云服务后台之间的标准连接方式,REST API已经得到了绝大部分开发者的认可和广泛的应用.近年来,在新兴API经济模式逐渐兴起,许多厂商纷纷将自己的后台业务能力作为REST ...
- mariadb 长链接时间限制导致队列消费进程崩溃
项目是一个数据同步项目,线下Android客户端把本地sqllite数据提交到云端队列,php做守护进程消费队列,以同步数据.初测没有问题,可是时不时出现诡异的崩溃,因为设置了错误邮件报警,发现错误代 ...
- 老李案例分享:定位JAVA内存溢出
老李案例分享:定位JAVA内存溢出 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.在poptest的loadrunner的培 ...
- listview展示倒计时
public class MainActivity extends Activity { /**截至时间数据源**/ private List<Date> listData; /**当前时 ...
- 谱聚类(Spectral clustering)(2):NCut
作者:桂. 时间:2017-04-13 21:19:41 链接:http://www.cnblogs.com/xingshansi/p/6706400.html 声明:欢迎被转载,不过记得注明出处哦 ...
- 轻量级操作系统FreeRTOS的内存管理机制(一)
本文由嵌入式企鹅圈原创团队成员朱衡德(Hunter_Zhu)供稿. 近几年来,FreeRTOS在嵌入式操作系统排行榜中一直位居前列,作为开源的嵌入式操作系统之一,它支持许多不同架构的处理器以及多种编译 ...
- Nginx配置同一个域名同时支持http与https两种方式访问
Nginx配置同一个域名http与https两种方式都可访问,证书是阿里云上免费申请的 server{listen 80;listen 443 ssl;ssl on;server_name 域名;in ...
- 装饰器模式(Decorator)——深入理解与实战应用
本文为原创博文,转载请注明出处,侵权必究! 1.初识装饰器模式 装饰器模式,顾名思义,就是对已经存在的某些类进行装饰,以此来扩展一些功能.其结构图如下: Component为统一接口,也是装饰类和被装 ...