简介
PipedWriter和PipedReader分别是字符管道输出流和字符管道输入流,是字符流中"管道流",可以实现同一个进程中两个线程之间的通信,与PipedOutputStream和PipedInputStream相比,功能类似.区别是前者传输的字符数据,而后者传输的是字节数据.
不同线程之间通信的流程大致是:PipedWriter与PipedReader流进行连接,写入线程通过将字符管道输出流写入数据,实际将数据写到了与PipedWriter连接的PipedReader流中的缓冲区,然后读取线程通过PipedReader流读取之前存储在缓冲区里面的字符数据.
PipedWriter介绍
1.构造方法
public PipedWriter(PipedReader snk){}
public PipedWriter() {}
- 有参构造方法,参数传入字符管道输入流,创建的是与指定字符管道输入流连接的字符管道输出流.
- 无参构造方法,创建的是没有任何连接的字符管道输出流.
2.内部变量
private PipedReader sink;
private boolean closed = false;
- 与字符管道输出流连接的字符管道输入流.
- 用于PipedWriter流是否关闭的标识.用于检查连接的状态.
3.内部方法
public synchronized void connect(PipedReader snk){}
public void write(int c){}
public void write(char cbuf[], int off, int len){}
public synchronized void flush(){}
public void close(){}
- connect(PipedReader snk)---字符管道输入流和字符管道输出流连接.
- write(int c)---将一个字符写入缓冲区中
- write(char cbuf[],int off,int len)---将字符数组cbuf中off位置开始,最多len个字符写到缓冲区.
- flush()---刷新缓冲区
- close()---关闭字符管道输出流,释放相关资源.
PipedReader介绍
1.构造方法
public PipedReader(PipedWriter src) {}
public PipedReader(PipedWriter src, int pipeSize) {}
public PipedReader() {}
public PipedReader(int pipeSize){}
- PipedReader(PipedWriter src)---创建与字符管道输出流连接的字符管道输入流.缓冲区大小为默认的1024个字符.
- PipedReader(PipedWriter src,int pipeSize)---创建指定缓冲区大小pipeSize,并与字符管道输出流连接的字符管道输入流.
- PipedReader()---无参构造方法,创建了未连接的字符管道输入流,缓冲区大小为默认的1024个字符,
- PipedReader(int pipeSize)---创建了未连接的字符管道输入流,缓冲区大小为指定的pipeSize.
2.内部变量
boolean closedByWriter = false;
boolean closedByReader = false;
boolean connected = false;
Thread readSide;
Thread writeSide;
char buffer[];
int in = -1;
int out = 0;
- closeByWriter---标识的是PipedWriter流是否关闭.
- closeByReader---标识的是PipedReader流是否关闭.
- connected---字符管道输入流和字符管道输出流是否连接.
- readSide---读取缓冲区数据的线程.
- writeSide---写入缓冲区数据的线程
- buffer---字符管道输入流中缓冲区,即为字符数组
- in---缓冲区的索引值,表示的是从PipedWriter接收到的字符数据存储到缓冲数组buffer中位置索引.
- out---缓冲区索引值,表示的是管道输入流中读取下一个字节的位置.
3.内部方法
public void connect(PipedWriter src){}
synchronized void receive(int c){}
synchronized void receive(char c[], int off, int len){}
synchronized void receivedLast() {}
public synchronized int read() {}
public synchronized int read(char cbuf[], int off, int len){}
public synchronized boolean ready(){}
public void close() {}
- connect(PipedWriter src)---字符管道输入流和字符管道输出流连接.
- receive(int c)---从字符管道输出流中接收单个字符数据,存储到字符管道输入流的缓冲区.
- receive(char c[],int off,int len)---从字符管道输出流中接收字符数组数据,存储到字符管道输入流的缓冲区.
- receivedLast()---通知所有等待线程,缓冲区所有的字符已被读取.
- read()---从缓冲区中读取一个字符数据.
- read(char cbuf[],int off, int len)---从缓冲区中读取len个字符数据到字符数组cbuf中,位置从off开始.
- ready()---字符管道输入流中缓冲区数据是否可读取.如不为空可读取,返回true.
- close()----关闭流,释放关联的资源.
案例
public class PipedWriteDemo {
public static void main(String[] args) throws IOException {
Sender sender = new Sender();
Receiver receiver = new Receiver();
PipedWriter pipedWriter= sender.getPipedWriter();
PipedReader pipedReader = receiver.getPipedReader();
pipedReader.connect(pipedWriter);
sender.start();
receiver.start();
}
}
public class Sender extends Thread {
private PipedWriter pipedWriter = new PipedWriter();
public PipedWriter getPipedWriter() {
return pipedWriter;
}
public void run() {
try {
pipedWriter.write("this is a demo about PipedWriter and PipedReader".toCharArray());
pipedWriter.append("0123");
pipedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class Receiver extends Thread{
private PipedReader pipedReader = new PipedReader();
public PipedReader getPipedReader() {
return pipedReader;
}
public void run() {
try {
char[] buf = new char[1024];
pipedReader.read(buf);
System.out.println(new String(buf));
pipedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果:
this is a demo about PipedWriter and PipedReader0123
源码分析
1.PipedWriter源码分析
public class PipedWriter extends Writer {
//与PipedWriter流连接的PipedReader
private PipedReader sink;
//PipedWriter流是否关闭的标识,主要是用于检查连接状态
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;
}
//将一个字符数据写到字符管道输出流中
public void write(int c) throws IOException {
if (sink == null) {
throw new IOException("Pipe not connected");
}
sink.receive(c);
}
//将字符数组cbuf中off位置开始,len个字节写到字符管道输出流中
//当读取线程关闭情况,会抛出异常
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();
}
}
}
2.PipedReader源码分析
public class PipedReader extends Reader {
boolean closedByWriter = false;//标识pipedWriter是否关闭
boolean closedByReader = false;//标识PipedReader是否关闭
boolean connected = false;//PipedWriter与PipedReader连接是否关闭
//读取缓冲区数据的线程
Thread readSide;
//向缓冲区写入数据的线程
Thread writeSide;
//默认缓冲区大小
private static final int DEFAULT_PIPE_SIZE = 1024;
//字符管道输入流中缓冲区,即字符数组buffer
char buffer[];
//注意的是in==out的时候,表明缓冲区已满
//接受从PipedWriter传过来的字符数据存储到缓冲数组buffer的索引位置,in=-1表示缓冲区是空的.
int in = -1;
//从pipedReader缓冲区中读取下一个字符的索引
int out = 0;
//创建一个管道输出流连接的,缓冲区大小为默认的1024个字符的PipedReader
public PipedReader(PipedWriter src) throws IOException {
this(src, DEFAULT_PIPE_SIZE);
}
//创建一个与管道输出流连接的,并且指定了缓冲区大小为pipeSize的PipedReader
public PipedReader(PipedWriter src, int pipeSize) throws IOException {
initPipe(pipeSize);
connect(src);
}
//无参构造方法,创建默认大小为1024个字符的缓冲区,与PipedWriter未连接
public PipedReader() {
initPipe(DEFAULT_PIPE_SIZE);
}
//有参构造方法,创建指定大小pipeSize的缓冲区,与PipedWriter未连接
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);
}
//从管道输出流中接收一个字符存储到管道输入流的缓冲区中
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");
}
/* full: kick any waiting readers */
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;
}
}
//从管道输出流中接收字符数组c,将off开始,len个字符存储到管道输入流的缓冲区
synchronized void receive(char c[], int off, int len) throws IOException {
while (--len >= 0) {
receive(c[off++]);
}
}
//"通知"所有等待线程,所有字符已被读取
synchronized void receivedLast() {
closedByWriter = true;
notifyAll();
}
//从管道输入流中读取一个字符,如果没有字符可读,将会返回-1.
//此方法会一直阻塞直到有字符写到缓冲区中.
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)) {
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++];
if (out >= buffer.length) {
out = 0;
}
//缓冲区所有的字符数据全被读取
if (in == out) {
/* now empty */
in = -1;
}
return ret;
}
//从管道输入流中读取最多len个字符到字符数组cbuf中的off位置开始
//如果存在剩余可读字符少于len情况,则实际读取的字符少于len个.
//缓冲区没有数据情况下,此方法会一直阻塞
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开始位置
cbuf[off] = (char)c;
int rlen = 1;
//已读取一个字符c,所以要减掉一个字符--len
while ((in >= 0) && (--len > 0)) {
cbuf[off + rlen] = buffer[out++];
rlen++;
if (out >= buffer.length) {
out = 0;
}
//in==out表示缓冲区数据已被读完
if (in == out) {
/* now empty */
in = -1;
}
}
return rlen;
}
//管道输入流中是否可读取,缓冲区不为空情况下,可读取,返回true
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;
}
}
总结
1.in表示的是接收到从PipedWriter传输过来的数据存储到PipedReader的缓存区的索引位置,out表示的是从缓冲区读取到下一字符的索引.当in==out 的时候,表示是的是缓冲区的所有数据全部被读取.
1.PipedWriter与PipedReader实现进行线程之间通信,写入线程写到PipedWriter的字符数据,实际是存储到与PipedWriter连接的PipedReader中缓冲区里面,然后通过读取线程读取到缓冲区里面的数据.