PipedInputStream 和 PipedOutputStream 源码解析

1 篇文章 0 订阅
1 篇文章 0 订阅

PipedInputStream 和 PipedOutputStream 源码解析

概要

  1. 位于 java.io 包中
  2. PipedInputStream 和 PipedOutputStream 为管道输入 输出流主要终于任务之间的通信。
  3. 不要在一个线程中同时使用PipeInpuStream和PipeOutputStream,这会造成死锁
  4. 基本原理:

    a. PipedInputStream 内部封装了一个 byte 数组,默认大小为 1024
    b. PipeOutputStream 向 byte 数组写数据,PipedInputStream 向byte数组读数据。

  5. 当这个缓冲数组已满的时候,输出流PipedOutputStream所在的线程将阻塞;
  6. 当这个缓冲数组首次为空的时候,输入流PipedInputStream所在的线程将阻塞。

一、PipedInputStream 源码详解

public class PipedInputStream extends InputStream {
    boolean closedByWriter = false;   // --------标记管道输出流是否关闭
    volatile boolean closedByReader = false;  // --------标记管道输出流是否关闭
    boolean connected = false; // --------标记管道输出流与管道输入流是连接
    
    Thread readSide;    // -----向管道读数据的线程
    Thread writeSide;   //------向管道写数据的线程

    private static final int DEFAULT_PIPE_SIZE = 1024;   //----缓存默认大小 byte[1024]
    protected static final int PIPE_SIZE = DEFAULT_PIPE_SIZE;   // --- 管道缓存大小为默认大小1024

    protected byte buffer[];  // ---管道循环缓存区
    
     //当 in == out 是表示缓存中的字节已经被全部读取
    protected int in = -1;  //---下一个写入字节的位置
    protected int out = 0; //---下一个读取字节的位置

    // PipedInputStream 构造方法
    public PipedInputStream(PipedOutputStream src) throws IOException {
        this(src, DEFAULT_PIPE_SIZE);
    }
    public PipedInputStream(PipedOutputStream src, int pipeSize)
            throws IOException {
         initPipe(pipeSize);
         connect(src);
    }
    public PipedInputStream() {
        initPipe(DEFAULT_PIPE_SIZE);
    }
    public PipedInputStream(int pipeSize) {
        initPipe(pipeSize);
    }
    //初始化缓冲区大小
    private void initPipe(int pipeSize) {
         if (pipeSize <= 0) {
            throw new IllegalArgumentException("Pipe Size <= 0");
         }
         buffer = new byte[pipeSize];
    }
    // 调用 PipedOutputStream 的 connect 方法
    public void connect(PipedOutputStream src) throws IOException {
        src.connect(this);
    }
    
    // 接收int类型的数据b。
    // 它只会在PipedOutputStream的write(int b)中会被调用
    protected synchronized void receive(int b) throws IOException {
        //检查管道的状态
        checkStateForReceive();
        //获得写入数据的线程
        writeSide = Thread.currentThread();
        
        //当管道中的数据全部被读取完时,则等待
        if (in == out)
            awaitSpace();
         //当 i< 0 时,说明还未读取这初始化输入输出的位置
        if (in < 0) {
            in = 0;
            out = 0;
        }
		// 存入 int 类型的底8位
        buffer[in++] = (byte)(b & 0xFF);
		//循环缓冲
        if (in >= buffer.length) {
            in = 0;
        }
    }
    // 接收字节数组b。
    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) {
                //下一次缓冲长度(可缓冲的长度)为 buffer.length - 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,则终止程序。
            assert(nextTransferAmount > 0);
            //写入到缓冲区
            System.arraycopy(b, off, buffer, in, nextTransferAmount);
            bytesToTransfer -= nextTransferAmount;
            off += nextTransferAmount;//下一次写入缓存区的位置
            in += nextTransferAmount;//移动写入指针
            //
            if (in >= buffer.length) {
                in = 0;
            }
        }
    }

    private void checkStateForReceive() throws IOException {
        if (!connected) {
            throw new IOException("Pipe not connected");
        } else if (closedByWriter || closedByReader) {
            throw new IOException("Pipe closed");
        } else if (readSide != null && !readSide.isAlive()) {
            throw new IOException("Read end dead");
        }
    }

    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();
            }
        }
    }

    //输出流不再写数据,输出流关闭时调用
    synchronized void receivedLast() {
        closedByWriter = true;
        notifyAll();
    }

//读取一个字节,并转成 int 类型
    public synchronized int read()  throws IOException {
        if (!connected) {
            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();
        int trials = 2;
        while (in < 0) {
            if (closedByWriter) {
                /* 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();
            try {
                wait(1000);
            } catch (InterruptedException ex) {
                throw new java.io.InterruptedIOException();
            }
        }
        int ret = buffer[out++] & 0xFF;
        if (out >= buffer.length) {
            out = 0;
        }
        if (in == out) {
            /* now empty */
            in = -1;
        }

        return ret;
    }

   //将此管道输入流中最多len个字节的数据读入一个字节数组。 
//如果到达数据流的末尾或者len超过管道的缓冲区大小,则将读取少于len个字节。
//如果len为零,则不读取任何字节,返回0;
//否则,该方法将阻塞直到至少1个字节的输入可用,已检测到流的末尾,或者抛出异常。
    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;

            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 available() throws IOException {
        if(in < 0)
            return 0;
        else if(in == out)
            return buffer.length;
        else if (in > out)
            return in - out;
        else
            return in + buffer.length - out;
    }

   //关闭输入流
    public void close()  throws IOException {
        closedByReader = true;
        synchronized (this) {
            in = -1;
        }
    }
}

1、receive(byte b[], int off, int len) 方法详解

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) {
                //下一次缓冲长度(可缓冲的长度)为 buffer.length - 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,则终止程序。
            assert(nextTransferAmount > 0);
            //写入到缓冲区
            System.arraycopy(b, off, buffer, in, nextTransferAmount);
            bytesToTransfer -= nextTransferAmount;
            off += nextTransferAmount;//下一次写入缓存区的位置
            in += nextTransferAmount;//移动写入指针
            //
            if (in >= buffer.length) {
                in = 0;
            }
        }
    }
a、循环缓冲模型介绍

循环缓冲模型

二、PipedOutputStream 源码详解

public
class PipedOutputStream extends OutputStream {

    private PipedInputStream sink; // ---关联输入流
    public PipedOutputStream(PipedInputStream snk)  throws IOException {
        connect(snk);
    }
    public PipedOutputStream() {
    }
    //等价于输入流中的connect方法,输入流中的connect方法就是调用了这个方法
    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;
    }

    //写入一个int类型,只能保存第八位 0 - 255
    public void write(int b)  throws IOException {
        if (sink == null) {
            throw new IOException("Pipe not connected");
        }
        //调用关联的输入流对象 向缓冲区输入 b
        sink.receive(b);
    }

    // 输入一个byte数组
    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();
        }
    }
}

三、应用—生产者消费者模式(线程之间的通信)

public class PC {

    PipedInputStream pipedInputStream = new PipedInputStream();
    PipedOutputStream pipedOutputStream = new PipedOutputStream();
    {
        try {
            pipedOutputStream.connect(pipedInputStream);
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        CP cp = new CP();
        Consumer consumer = cp.new Consumer();
        Producer producer = cp.new Producer();
        producer.start();
        consumer.start();
    }

    class Producer extends Thread{

        @Override
        public void run() {
            try {
                for (int i = 0; i < 10; i++){

                    pipedOutputStream.write(i);
                    System.out.println("Producer product:"+i);
                    pipedOutputStream.flush();
                    Thread.sleep(1000);
                }

            } catch (IOException e) {
                e.printStackTrace();
            }catch (/*Interrupted*/Exception e){
                e.printStackTrace();
            }finally {
                try {
                    pipedOutputStream.close();

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class Consumer extends Thread{

        @Override
        public void run() {
            try {
                while (true){

                    int a = pipedInputStream.read();
                    if(a != -1){
                        System.out.println("Consumer consume:"+a);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    pipedOutputStream.close();
                    pipedInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

运行结果:

Consumer consume:0
Producer product:0
Producer product:1
Consumer consume:1
Producer product:2
Consumer consume:2
Producer product:3
Consumer consume:3
Producer product:4
Consumer consume:4
Producer product:5
Consumer consume:5
Producer product:6
Consumer consume:6
Producer product:7
Consumer consume:7
Producer product:8
Consumer consume:8
Producer product:9
Consumer consume:9

此时消费者线程处于阻塞状态,而生产者线程已经死亡。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值