该类继承自InputStream
无引入包
该类的类头注释如下:
/** * A piped input stream should be connected * to a piped output stream; the piped input * stream then provides whatever data bytes * are written to the piped output stream. * Typically, data is read from a <code>PipedInputStream</code> * object by one thread and data is written * to the corresponding <code>PipedOutputStream</code> * by some other thread. Attempting to use * both objects from a single thread is not * recommended, as it may deadlock the thread. * The piped input stream contains a buffer, * decoupling read operations from write operations, * within limits. * A pipe is said to be <a name="BROKEN"> <i>broken</i> </a> if a * thread that was providing data bytes to the connected * piped output stream is no longer alive. * * @author James Gosling * @see java.io.PipedOutputStream * @since JDK1.0 */
大意如下:
一个管道输入流需要与管道输出流链接
管道输入流会获得管道出流被写入的任意byte数据
典型情况下,数据通过PipedInputStream被读取(单线程)并且数据被对应的PipedOutputSream写出
尝试通过单线程使用这两个对象是不推荐的
可能会造成线程的死锁
这个管道输入流包括了一个缓冲区,在缓冲区内可以分离读操作和写操作
如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏
该类含有如下的成员变量:
写关闭标志位
boolean closedByWriter = false;
读关闭标志位(要求每次都读取,敏感信息)
volatile boolean closedByReader = false;
连接状态
boolean connected = false;
读线程
Thread readSide;
写线程
Thread writeSide;
默认缓冲区大小
private static final int DEFAULT_PIPE_SIZE = 1024;
共用的管道缓冲区大小
protected static final int PIPE_SIZE = DEFAULT_PIPE_SIZE;
循环缓冲区
protected byte buffer[];
读取指针的偏移量
protected int in = -1;
写出指针的偏移量
protected int out = 0;
该类含有如下的成员方法:
构造函数(默认缓冲区大小
public PipedInputStream(PipedOutputStream src) throws IOException { this(src, DEFAULT_PIPE_SIZE); }
构造函数(指定缓冲大小
public PipedInputStream(PipedOutputStream src, int pipeSize) throws IOException { initPipe(pipeSize); connect(src); }
构造函数(暂时无链接,默认大小
public PipedInputStream() { initPipe(DEFAULT_PIPE_SIZE); }
构造函数(同上,指定大小
public PipedInputStream(int pipeSize) { initPipe(pipeSize); }
管道初始化函数(构造函数提取出的公因数
private void initPipe(int pipeSize) { if (pipeSize <= 0) {//检查缓冲区大小合理性 throw new IllegalArgumentException("Pipe Size <= 0"); } buffer = new byte[pipeSize];//初始化缓冲区 }
链接两个未被链接的管道(如果已链接就抛IOException
public void connect(PipedOutputStream src) throws IOException { src.connect(this); }
接收一个byte数据(该方法会被阻塞,在没有有效输入时
protected synchronized void receive(int b) throws IOException { checkStateForReceive();//检查接收状态 writeSide = Thread.currentThread();//获得当前的操作线程 if (in == out)//如果写入指针和写出指针指向一个地方表示当前缓冲区满,无法接收 awaitSpace(); if (in < 0) {//如果输入指针小于0(为空),则初始化 in = 0; out = 0; } buffer[in++] = (byte)(b & 0xFF);//将输入byte写入缓冲区 if (in >= buffer.length) {//检查输出指针是否溢出,溢出则归0,可以使用in%=buffer.length,不过这样的操作效率就会变低 in = 0; } }
接收一个数组数据(也会被阻塞
synchronized void receive(byte b[], int off, int len) throws IOException { checkStateForReceive(); writeSide = Thread.currentThread(); int bytesToTransfer = len;//获得需要写入的数据长度 while (bytesToTransfer > 0) {//长度有效 if (in == out)//满了 awaitSpace(); int nextTransferAmount = 0;//下次最多可写入的长度(因为可能存在写入长度超过缓冲区长度,所以分多次写入,每次写入都会覆盖上次的数据,需要在写入之前读取出上次的数据) if (out < in) {//in指针比较靠前 nextTransferAmount = buffer.length - in;//获得剩余填充的数值长度 } else if (in < out) { if (in == -1) {//缓冲区为空 in = out = 0; nextTransferAmount = buffer.length - in;//获得缓冲区长度 } else { nextTransferAmount = out - in;//获得可读取长度(如果读取到out就证明缓冲区已经读满了,再读入会影响数据 } } if (nextTransferAmount > bytesToTransfer)//可一次写入 nextTransferAmount = bytesToTransfer; assert(nextTransferAmount > 0);//断言大于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 { while (in == out) { checkStateForReceive();//检测有效性 /* full: kick any waiting readers */ notifyAll();//唤醒所有阻塞线程(实质只找一个,但是赋予所有线程争夺权 try { wait(1000);//挂起线程(丢失锁的竞争权 } catch (InterruptedException ex) {//抛IO中断 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) {//写入流关闭,标识文件已经读完(空文件 /* closed by writer, return EOF */ return -1; } if ((writeSide != null) && (!writeSide.isAlive()) && (--trials < 0)) {//写线程存在且并未活动且标志位小于0,管道断裂 throw new IOException("Pipe broken"); } /* might be a writer waiting */ notifyAll();//唤醒所有的线程来竞争锁 try { wait(1000);//挂起该读取方法 } catch (InterruptedException ex) { throw new java.io.InterruptedIOException(); } } int ret = buffer[out++] & 0xFF;//读出单个byte 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)) {//缓冲区不为空且剩余需读取长度部不为0 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) { /* now empty */ in = -1; } } return rlen;//返回实际读取长度 }
返回可以不受阻塞地读取出的数值数
public synchronized int available() throws IOException { if(in < 0) return 0; else if(in == out)//当两个相同时则为满(因为如果输出操作将指针修改成相等状态时会将in修改为-1,标志为空) 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; } }
该类实质是一个带有线程同步的循环数组,理解这个之后其实非常简单。该类主要用于进程间通信,需要注意的是一个管道必须要有两端才能成立,否则是不成立的。文中的写入写出输入输出可能会看的头晕,在阅读是可以思考一下,这个操作的主语是谁,是谁在操作,操作的又是谁,这些说法是从主语角度来写的。