基本概念
PipedReader& PipedWriter(管道字符输入流&管道输符字节流)是配套使用的。可以将管道输出流连接到管道输入流来创建通信管道。
详细用法参见管道字节流
继承关系:
源码探究
2.PipedReader
类结构图
成员变量,同 PipedInputStream,这里不再分析
private static final int DEFAULT_PIPE_SIZE = 1024;
boolean closedByWriter = false;
boolean closedByReader = false;
boolean connected = false;
Thread readSide;
Thread writeSide;
char buffer[];
int in = -1;
int out = 0;
该类定义了4个构造函数,可分为两种,未连接管道输出流与连接管道输出流。观察代码,构造函数主要做了两件事:
调用了 initPipe 方法来进行初始化,而该方法的主要作用是创建管道换区,如果未指定大小时,默认为 1024。
调用了 connect 方法来连接管道输出流,而该方法是在 PipedWriter 类中定义的,。它修改了输入流的成员变量值,令 in = -1; out = 0; connected = true;
public PipedReader(PipedWriter src) throws IOException {
this(src, DEFAULT_PIPE_SIZE);
}
public PipedReader(PipedWriter src, int pipeSize) throws IOException {
initPipe(pipeSize);
connect(src);
}
public PipedReader() {
initPipe(DEFAULT_PIPE_SIZE);
}
public PipedReader(int pipeSize) {
initPipe(pipeSize);
}
// 初始化
private void initPipe(int pipeSize) {
if (pipeSize <= 0) {
throw new IllegalArgumentException("Pipe size <= 0");
}
buffer = new char[pipeSize];
}
// 管道连接
public void connect(PipedWriter src) throws IOException {
src.connect(this);
}
下面来看 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) {
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++];
//读取位置已到达导管末尾,重置,开始下一轮的读取
if (out >= buffer.length) {
out = 0;
}
//表示管道中没有可读内容
if (in == out) {
in = -1;
}
return ret;
}
public synchronized int read(char cbuf[], int off, int len) 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");
}
if ((off < 0) || (off > cbuf.length) || (len < 0) || ((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
//先读取一个字符,判断管道中是否还有数据可读
int c = read();
if (c < 0) {
return -1;
}
cbuf[off] = (char) c;
int rlen = 1;
//与 PipedInputStream 不同,本质也是按字符读取
while ((in >= 0) && (--len > 0)) {
cbuf[off + rlen] = buffer[out++];
rlen++;
if (out >= buffer.length) {
out = 0;
}
if (in == out) {
in = -1;
}
}
return rlen;
}
重点来看 receive方法, 用于输入流接收信息,在输出流 write 操作时会被调用,我们也可以将其是为输出流的写入操作,只不过这里在输入流实现(因为是线程安全的方法,synchronized 生效的前提操作同一对象)。同样也有定义了两种常见的写入(接收)操作。
synchronized void receive(int c) 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");
}
//取得操作输入流的线程
writeSide = Thread.currentThread();
//表示管道(缓冲区)中数据刚好被全部读取完了
while (in == out) {
if ((readSide != null) && !readSide.isAlive()) {
throw new IOException("Pipe broken");
}
//唤醒其他阻塞线程,当前线程进入阻塞
notifyAll();
try {
wait(1000);
} catch (InterruptedException ex) {
throw new java.io.InterruptedIOException();
}
}
//初始化操作
if (in < 0) {
in = 0;
out = 0;
}
//将字节写入管道
buffer[in++] = (char) c;
//管道已满,重置
if (in >= buffer.length) {
in = 0;
}
}
synchronized void receive(char c[], int off, int len) throws IOException {
while (--len >= 0) {
//与管道输入流不同,这里调用 receive(int c) 来实现
receive(c[off++]);
}
}
synchronized void receivedLast() {
closedByWriter = true;
notifyAll();
}
最后来看看剩余的方法
public synchronized boolean ready() 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");
}
if (in < 0) {
return false;
} else {
return true;
}
}
public void close() throws IOException {
in = -1;
closedByReader = true;
}
}
2.PipedWriter
类结构图
成员变量
private PipedReader sink;
private boolean closed = false;
构造函数除了默认的无参构造函数外,还有一个带参的构造函数。在其内部调用了管道流连接的方法。
public PipedWriter(PipedReader snk) throws IOException {
connect(snk);
}
public PipedWriter() {
}
//关键 --> 管道流连接
public synchronized void connect(PipedReader snk) throws IOException {
if (snk == null) {
throw new NullPointerException();
} else if (sink != null || snk.connected) {
throw new IOException("Already connected");
} else if (snk.closedByReader || closed) {
throw new IOException("Pipe closed");
}
sink = snk;
snk.in = -1;
snk.out = 0;
snk.connected = true;
}
接着来看 write 方法,定义了两种 write 方式,并且都通过输入流接收信息
public void write(int c) throws IOException {
if (sink == null) {
throw new IOException("Pipe not connected");
}
sink.receive(c);
}
public void write(char cbuf[], int off, int len) throws IOException {
if (sink == null) {
throw new IOException("Pipe not connected");
} else if ((off | len | (off + len) | (cbuf.length - (off + len))) < 0) {
throw new IndexOutOfBoundsException();
}
sink.receive(cbuf, off, len);
}
最后再来看看剩下的方法
public synchronized void flush() throws IOException {
if (sink != null) {
if (sink.closedByReader || closed) {
throw new IOException("Pipe closed");
}
synchronized (sink) {
sink.notifyAll();
}
}
}
public void close() throws IOException {
closed = true;
if (sink != null) {
sink.receivedLast();
}
}