Java IO系列6 字节流之PipedInputStream与PipedOutputStream

用于不同线程之间的通信,如果我们在一个线程中同时使用会发生什么呢?

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次写入数据结束”是因为发生了死锁。
先看源码

public
class 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;
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值