/**
* InputStream --> FilterInputStream --> (1.DataInputStream 2.BufferedInputStream 3.PushInputStream)
*
* DataInputStream:以与机器无关的方式来读取java的基础类型
*
* BufferedInputStream:由于基础输入流InputStream一个字节一个字节取读取,频繁与磁盘进行交互,造成读取速度较低,缓冲流的存在就是先将数据流读取到缓冲流中(也就是内存中),然后一次性从内存读取多个字符,提高读取的效率
*
*
* PushInputStream:回退输入流 java中读取流的方式是顺序读取,如果某个数据不需要读取,这个类就可以将某些不需要的数据回退到缓冲区
*/
/**
* FilterInputStream的所有子类都是为基础流提供一些额外的功能,为什么不直接继承了,非要来继承FilterInputStream。这就设计到了装饰者模式和继承的区别了
*/
/**
* 装饰着模式动态的将责任附加到对象上,若要增加功能,装饰者提供了比继承更具有弹性的替代方案
*
* 来 我来理解一下抽象组件,具体组件,抽象装饰者和具体装饰者
*
* 抽象组件:InputStream 具体组件FileInputStream等
*
*
* 抽象装饰者:FilterInputStream 具体装饰者 DataInputStream,BufferedInputStream
*/
/**
* 为什么具体组件和装饰者需要实现自抽象组件来保持类型一致
* 装饰者的优缺点
* 可以通过其他什么模式来避免装饰者的不足
*/
/**
* SocketOutputStream继承自FileOutputStream 充分说明了Socket也就是一个文件描述符
*/
/**
* 管道输入输出流PipedInputStream和PipedOutputStream
*
* 用处:通过管道进行线程间的通信
* 在线程A中向PipedOutputStream中写入数据,这些数据会自动发送到与PipedOutputStream对应的PipedInputStream中,进而存储在对应的缓冲区中
* 线程B通过读取PipedInputStream中的数据,来实现线程中的通信
*/
/**
* 灵魂拷问1:为什么字节输入流读取一个字节,返回的却是int类型,而不是一个byte类型
*
* 灵魂回答:字节输入流可以操作任意类型的的文件,比如音频图片等,这些文件底层都是以二进制形式的存储的,如果每次读取都返回byte,有可能在读到中间的时候遇到11111111(文件底层按补码来存储的)
*
* -1的源码 1000 0001
*
* -1的反码 1111 1110
*
* -1的补码 1111 1111
*
* 如果用int来表示的话
*
* 很明显-1自己就得在前面再加上三个字节 0000 0000 0000 0000 0000 0000 1111 1111 (255)
*
* 这样子就算读到中间的时候读到了-1表示的也不会返回,因为是按照int拼接之后返回的
*
* 但是结束标记的-1就是int类型表示的。
*
* 灵魂拷问2:为什么前面的数据都是一个字节一个字节的访问,结束符就是直接访问int类型了
*
* *********************************************************************************************
* 答:这个答案有点勉为其难,说什么结束标志的-1就是int类型。底层的数据读取机制看来还是很复杂的
* *********************************************************************************************
*/
PipedInputStream和PipedOutputStream源码
关联方法
pipedInputStream.connect(pipedOutputStream);
或者:pipedOutputStream.connect(pipedInputStream);
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;
}
PipedOutputStream:
private PipedInputStream sink;
pipedOutputStream.write(message.getBytes());
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
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); //很明显write的数据最终都会被所关联的PipedInputStream所消费掉(receive)
}
PipedInputStream
*************************************************************
/** 在read的时候,如果Buffer里面没有数据,会阻塞
*
*/
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;
// in:next byte of data will be stored when received from the connected
// out:next byte of data will be read by this piped input stream
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 read() throws IOException {
if (!connected) { // 判断是否连接了OutputStream
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(); // 获取到InputStream线程
int trials = 2;
while (in < 0) { // in<0 说明buffer里面没有数据
if (closedByWriter) { // 没有数据而且被OutputStream主动关闭了,肯定直接返回-1结束了
/* 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(); // 这里是唤醒写等待的,也就是OutputStream所属的线程,他们在buffer被写满的情况下会等待
try {
// 因为wait方法得调用,当前线程已经释放掉了锁,所以被notifyAll醒得线程就不需要等到同步代码块结束被被动释放得锁了,可以直接获取锁了,因为锁在同步代码块中就已经别释放掉了
wait(1000); // 这里是读等待,也就是InputStream所属的线程,他们在buffer为空,也就是in<0的情况下等待
} catch (InterruptedException ex) {
throw new java.io.InterruptedIOException();
}
}
// 从buffer里面取出来out对应的字节数据之后再把out加上1,方便后面判断buffer是否在这次取完之后就空了
int ret = buffer[out++] & 0xFF;
if (out >= buffer.length) {
out = 0;
}
if (in == out) {
/* now empty */
in = -1;
}
return ret;
}
*******************************************************************************
// 执行这个方法的线程是OutputStream所属的线程
synchronized void receive(byte b[], int off, int len) throws IOException {
checkStateForReceive();
writeSide = Thread.currentThread();
int bytesToTransfer = len;
while (bytesToTransfer > 0) {
if (in == out) // 如果in==out说明buffer是满的,那么写线程就得等待
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) // 这里还判断了一下buffer里面可供放置字节数据的地方是否能够容纳当前写入的数据
nextTransferAmount = bytesToTransfer;
assert(nextTransferAmount > 0);
System.arraycopy(b, off, buffer, in, nextTransferAmount);
bytesToTransfer -= nextTransferAmount; // 剩余多少,下次继续存入
off += nextTransferAmount;
in += nextTransferAmount;
if (in >= buffer.length) { // 如果in的值大于了当前buffer数组的长度,那么下标置为0,表示当前有数据可以读取,而且也可以从下标0开始继续写,感觉就是一个循环覆盖的过程,所以超过buffer字节大小的数据,它会重复覆盖掉之前的数据插入。
in = 0;
}
}
}
***********************************************************************
private void awaitSpace() throws IOException {
while (in == out) { // 这里持续阻塞啊 ,死循环,除非in != out 表示buffer有空闲的地方
checkStateForReceive();
/* full: kick any waiting readers */
notifyAll(); // 因为有数据,所以一直在通知等待在这个InputStream上面的读线程来读数据
try {
wait(1000); // 这里放出锁1000ms来给其他的对象获取做对应的操作
} catch (InterruptedException ex) {
throw new java.io.InterruptedIOException();
}
}
}