一、PipedWriter介绍
PipedWriter是字符管道输出流,继承自Writer,功能与PipedOutputStream类似,通过与PipedReader组合使用实现类似管道的功能,在多线程环境下,一个线程使用PipedWriter写入数据,通过管道将数据传递到管道另一端的字符输入流内部缓冲区(实际是通过当前字符管道输出流绑定的字符管道输入流对象sink的receive方法将数据存储到字符输入流的内部字符缓冲区实现的),其他线程去读该内部缓冲区的字符数据,实现线程间通信。
二、PipedWriter类成员变量
package java.io;
public class PipedWriter extends Writer {
/** 管道绑定的字符输入流对象 **/
private PipedReader sink;
/*
* 当前管道字符输出流的状态,独立于管道另一端字符输入流的状态
*/
private boolean closed = false;
}
三、PipedWriter源码分析
1 - 构造函数
/**
* 构造函数,调用connect为当前对象与指定字符管道输入流对象建立连接
*/
public PipedWriter(PipedReader snk) throws IOException {
connect(snk);
}
/**
* 构造函数,空实现,还没与管道的字符输入流建立连接,在使用之前必须先建立连接无论是管道数据接收端PipedReader还是
* 还是数据写入端PipedWriter调用connect建立连接
*/
public PipedWriter() {
}
PipedWriter的构造函数逻辑非常简单,两种构造函数一种什么都没做只是创建一个未建立管道连接的对象,另一个构造函数在实例化的同时调用connect创建了与指定字符管道输入流对象的管道连接,之后PipedWriter主要是基于这个指定的管道输入流对象操作管道数据。
2 - void write(int c)方法 - 写入单个字符
public void write(int c) throws IOException {
if (sink == null) {
throw new IOException("Pipe not connected");
}
sink.receive(c);
}
方法逻辑很简单就是在开头做了管道绑定的接收端字符管道输入流对象sink的判空,若不为空则调用它的receive方法接收写入的单个字符c,receive的内部方法逻辑可参照上篇PipedReader对于receive方法的解析
3 - 其他成员方法
/**
* 为当前管道输出流对象连接指定的管道输入流对象snk,若当前管道输出流之前已经建立与其他字符管道输入流的连接则抛出
* IOException异常
*/
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;
}
/**
* 将指定字符数组cbuf从off位置开始len个字符写入管道绑定的字符管道输入流对象内部缓冲区中
* 如果一个线程正在读取连接的字符管道输入流内部缓冲区字节数据,这时候当前线程挂掉那么会抛出一个IO异常
*/
public void write(char cbuf[], int off, int len) throws IOException {
//管道绑定的字符管道输入流对象为空
if (sink == null) {
throw new IOException("Pipe not connected");
}
/** 校验字符数组起始位置参数off **/
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();
}
}
}
/**
* 关闭当前管道输出流对象,释放与此有关的系统资源,实际是调用绑定的管道输入流对象sink的reveivedLast方法
* 更新sink管道写入端状态成员变量closedByWriter并唤醒其他等待线程
*/
public void close() throws IOException {
closed = true;
if (sink != null) {
sink.receivedLast();
}
}