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
原创粉丝点击