Java IO系列6 字节流之PipedInputStream与PipedOutputStream
来源:互联网 发布:淘宝自创品牌怎么做 编辑:程序博客网 时间:2024/06/06 17:15
用于不同线程之间的通信,如果我们在一个线程中同时使用会发生什么呢?
PipedInputStream inputStream = new PipedInputStream(); //默认1024字节的缓冲区 //第一种连接方式 //PipedOutputStream outputStream = new PipedOutputStream(inputStream); PipedOutputStream outputStream = new PipedOutputStream(); try { //第二种连接方式 outputStream.connect(inputStream);//或者inputStream.connect(outputStream); byte[] data=new byte[1000]; byte[] store=new byte[50]; Arrays.fill(data, (byte)1); int count=1; outputStream.write(data,0,data.length); while(count<2){ System.out.println("第"+count+"次读取数据"); inputStream.read(store, 0, store.length); //每次读50字节数据 System.out.println("第"+count+"次读取数据结束"); System.out.println("第"+(count+1)+"次写入数据"); outputStream.write(data);//每次写1000字节数据 System.out.println("第"+(count+1)+"次写入数据结束"); count++; } } catch (IOException e) { e.printStackTrace(); }
运行结果:
没有“第2次写入数据结束”是因为发生了死锁。
先看源码
publicclass PipedOutputStream extends OutputStream { private PipedInputStream sink; //如果使用这种构造函数,就会自动连接 public PipedOutputStream(PipedInputStream snk) throws IOException { connect(snk); } public PipedOutputStream() { } //连接过后,不可以再次连接 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; } //写入一个字节,PipedInputStream 就会接受一个字节,把该字节放到缓存中 public void write(int b) throws IOException { if (sink == null) { throw new IOException("Pipe not connected"); } sink.receive(b); } 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); } public synchronized void flush() throws IOException { if (sink != null) { synchronized (sink) { sink.notifyAll(); } } } public void close() throws IOException { if (sink != null) { sink.receivedLast(); } }}
先看看PipedInputStream的成员变量
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; //PipedOutputStream write写入字节时会通过PipedInputStream的receive方法接受 //然后存到buffer中 protected byte buffer[]; //-1表示buffer是一个空的(空的不代表里面没数据) //receive方法会改变该值 //虽然read也可以改变该值,但是仅仅出现在 in == out的时候,表示里面的数据已经读完的,把in重新 置为-1,那么下次write时,in =0,又会从buffer的第一个位置开始写入数据 protected int in = -1; //表示PipedInputStream读取字节的位置 //如果buffer 是一个没有数据的空数组,PipedInputStream在read的时候就会等待PipedOutputStream写入数据 //如果buffer里已经有数据,read的时候out就会++,一路追赶in所在的位置,当in == out表示buffer里的数据已经读完,就会把in =-1 protected int out = 0;
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); }
首次写入1000个字节的数据: outputStream.write(data,0,data.length);
第一次读50个字节后 :inputStream.read(store, 0, store.length); //每次读50字节数据
紧接着我们又向buffer里写了1000个字节: outputStream.write(data);//每次写1000字节数据
因为第一个写1000个字节后,还剩24个字节,再加上又读了50字节,当再次写入数据时,先把后面的24个字节填充完,发现in >= buffer.length,但是还有1000-24个字节要写入,这时in被置为0,in从0追赶out,但是也只能写入50个字节(因为这时 in == out,写线程等待)
在这出了问题
因为 if (in == out) awaitSpace();
receive方法运行在PipedOutputStream所在线程,该线程先要获取PipedOutputStream的对象的同步锁,
因为是单线程,第二次写入1000个字节时,只有该线程拿到了同步锁, 所以awaitSpace()一直在死循环。
private void awaitSpace() throws IOException { while (in == out) { checkStateForReceive(); /* full: kick any waiting readers */ notifyAll(); try { wait(1000); } catch (InterruptedException ex) { throw new java.io.InterruptedIOException(); } } }
//注意:receive方法是运行在PipedOutputStream所在的线程//先获取PipedOutputStream实例对象的监视器 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) { 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) nextTransferAmount = bytesToTransfer; assert(nextTransferAmount > 0); System.arraycopy(b, off, buffer, in, nextTransferAmount); bytesToTransfer -= nextTransferAmount; off += nextTransferAmount; in += nextTransferAmount; if (in >= buffer.length) { in = 0; } } }
0 0
- Java IO系列6 字节流之PipedInputStream与PipedOutputStream
- Java IO - PipedOutputStream & PipedInputStream
- java(5)--IO流之PipedInputStream和PipedOutputStream
- Java IO--管道流PipedOutputStream/PipedInputStream
- 【Java-IO】PipedInputStream和PipedOutputStream
- 黑马程序员——Java IO—字节流—PipedInputStream和PipedOutputStream
- java 管道流之PipedOutputStream和PipedInputStream
- JAVA IO系列----PipedInputStream与PipedOutputStream类,PipedWriter和PipedReader类
- java io系列04之 管道(PipedOutputStream和PipedInputStream)的简介,源码分析和示例
- java io系列04之 管道(PipedOutputStream和PipedInputStream)的简介,源码分析和示例
- Java-IO之管道(PipedInputStream和PipedOutputStream)
- IO 输入与输出(3) -- 节点流之四PipedInputStream和PipedOutputStream
- Java IO流 PipedInputStream类和PipedOutputStream类
- 【java基础:IO】管道流PipedInputStream和PipedOutputStream的Demostration
- java io 笔记三:PipedOutputStream、PipedInputStream类
- Java IO(八):PipedInputStream & PipedOutputStream
- java IO笔记(PipedInputStream/PipedOutputStream)
- 管道流PipedInputStream与PipedOutputStream操作实例
- ios AFNetWorking 数据缓存 (没看懂暂存)
- Swift读取txt格式的数据并转换成Double格式
- C++编译器
- JAVA常用类之包装类
- StaticCells静态单元格
- Java IO系列6 字节流之PipedInputStream与PipedOutputStream
- 如何选择悲观锁策略
- Java多线程知识点大集合
- 风投们进入区块链领域的正确姿势(知识)
- const关键字
- URLREWRITE的使用
- 黑马程序员——java基础之继承、抽象、接口
- linux里查看mysql的一些命令
- 51NOD 1083 矩阵取数问题