Java IO:PipedOutputStream和PipedInputStream使用详解及源码分析

版权声明:欢迎转载、引用 https://blog.csdn.net/sk199048/article/details/51222719

1 使用方法

  PipedOutputStream和PipedInputStream是管道输出流和管道输入流,配合使用可以实现线程间通信。
  使用管道实现线程间通信的主要流程如下:建立输出流out和输入流in,将out和in绑定,out中写入的数据则会同步写入的in的缓冲区(实际情况是,out中写入数据就是往in的缓冲区写数据,out中没有数据缓冲区)。

1.1 方法介绍

  PipedOutputStream提供的API如下:

//构造函数
public PipedOutputStream(PipedInputStream snk);
public PipedOutputStream();

public synchronized void connect(PipedInputStream snk); //将PipedOutputStream 和 PipedInputSteam绑定
public void write(int b); //向output写入b
public void write(byte b[], int off, int len); //向output写入字节数组b

public synchronized void flush();//刷新缓冲区,通知其他input读取数据
public void close();// 关闭
PipedOutputStream提供的API如下:
//构造函数
public PipedInputStream(PipedOutputStream src);
public PipedInputStream(PipedOutputStream src, int pipeSize);

public void connect(PipedOutputStream src); //将PipedOutputStream 和 PipedInputSteam绑定
protected synchronized void receive(int b); //向input缓冲区写入b
synchronized void receive(byte b[], int off, int len); //向input写入字节数组b

public synchronized int read(); //读取缓冲区下一个字节
public synchronized int read(byte b[], int off, int len) //读取缓冲区字节数组到b
public synchronized int available();// 缓冲区可读字节数组的个数
public void close(); // 关闭

1.2 使用示例

/**
 * 生产者线程
 */
public class Producer extends Thread {
    //输出流
    private PipedOutputStream out = new PipedOutputStream();

    //构造方法
    public Producer(PipedOutputStream out) {
        this.out = out;
    }
    @Override
    public void run() {
        writeMessage();
    }

    private void writeMessage() {
        StringBuilder sb = new StringBuilder("Hello World!!!");
        try {
            out.write(sb.toString().getBytes());
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
/**
 * 消费线程
 */
public class Consumer extends Thread {
    //输入流, 默认缓冲区大小为1024
    private PipedInputStream in = new PipedInputStream();

    //构造方法
    public Consumer(PipedInputStream in) {
        this.in = in;
    }

    @Override
    public void run() {
        readMessage();
    }
    private void readMessage() {
        byte [] buf = new byte[1024];
        try {
            int len = in.read(buf);
            System.out.println("缓冲区的内容为: " + new String(buf, 0, len));
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }
    }
}

@org.junit.Test
public void testPiped() {
    /**
     * 流程
     * 1 建立输入输出流
     * 2 绑定输入输出流
     * 3 向缓冲区写数据
     * 4 读取缓冲区数据
     */
    PipedOutputStream out = new PipedOutputStream();
    PipedInputStream in = new PipedInputStream();
    Producer producer = new Producer(out);
    Consumer consumer = new Consumer(in);

    try {
        out.connect(in);
        producer.start();
        consumer.start();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

  运行结果如下:

缓冲区的内容为: Hello World!!!

2 源码分析

  按照演示程序运行过程分析源码,主要有构造方法、connect、out写、in读等。

2.1 PipedOutputStream构造方法

/**
 * Creates a piped output stream connected to the specified piped
 * input stream. Data bytes written to this stream will then be
 * available as input from <code>snk</code>.
 *
 * @param      snk   The piped input stream to connect to.
 * @exception  IOException  if an I/O error occurs.
 */
public PipedOutputStream(PipedInputStream snk)  throws IOException {
    connect(snk);
}

/**
 * Creates a piped output stream that is not yet connected to a
 * piped input stream. It must be connected to a piped input stream,
 * either by the receiver or the sender, before being used.
 *
 * @see     java.io.PipedInputStream#connect(java.io.PipedOutputStream)
 * @see     java.io.PipedOutputStream#connect(java.io.PipedInputStream)
 */
public PipedOutputStream() {
}

2.2 PipedInputStream构造方法

/**
 * Creates a <code>PipedInputStream</code> so that it is
 * connected to the piped output stream
 * <code>src</code> and uses the specified pipe size for
 * the pipe's buffer.
 * Data bytes written to <code>src</code> will then
 * be available as input from this stream.
 *
 * @param      src   the stream to connect to.
 * @param      pipeSize the size of the pipe's buffer.
 * @exception  IOException  if an I/O error occurs.
 * @exception  IllegalArgumentException if {@code pipeSize <= 0}.
 * @since      1.6
 */
public PipedInputStream(PipedOutputStream src, int pipeSize)
        throws IOException {
    initPipe(pipeSize);
    connect(src);
}
public PipedInputStream(PipedOutputStream src) throws IOException {
    this(src, DEFAULT_PIPE_SIZE);
}

/**
 * Creates a <code>PipedInputStream</code> so that it is not yet
 * {@linkplain #connect(java.io.PipedOutputStream) connected} and
 * uses the specified pipe size for the pipe's buffer.
 * It must be {@linkplain java.io.PipedOutputStream#connect(
 * java.io.PipedInputStream)
 * connected} to a <code>PipedOutputStream</code> before being used.
 *
 * @param      pipeSize the size of the pipe's buffer.
 * @exception  IllegalArgumentException if {@code pipeSize <= 0}.
 * @since      1.6
 */
public PipedInputStream(int pipeSize) {
    initPipe(pipeSize);
}
public PipedInputStream() {
    initPipe(DEFAULT_PIPE_SIZE);
}

2.3 PipedOutputStream connect方法

/**
 * Connects this piped output stream to a receiver. If this object
 * is already connected to some other piped input stream, an
 * <code>IOException</code> is thrown.
 * <p>
 * If <code>snk</code> is an unconnected piped input stream and
 * <code>src</code> is an unconnected piped output stream, they may
 * be connected by either the call:
 * <blockquote><pre>
 * src.connect(snk)</pre></blockquote>
 * or the call:
 * <blockquote><pre>
 * snk.connect(src)</pre></blockquote>
 * The two calls have the same effect.
 *
 * @param      snk   the piped input stream to connect to.
 * @exception  IOException  if an I/O error occurs.
 */
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; //设置连接状态
}

2.4 PipedOutputStream write方法

/**
 * Writes the specified <code>byte</code> to the piped output stream.
 * <p>
 * Implements the <code>write</code> method of <code>OutputStream</code>.
 *
 * @param      b   the <code>byte</code> to be written.
 * @exception IOException if the pipe is <a href=#BROKEN> broken</a>,
 *          {@link #connect(java.io.PipedInputStream) unconnected},
 *          closed, or if an I/O error occurs.
 */
public void write(int b)  throws IOException {
    if (sink == null) {
        throw new IOException("Pipe not connected");
    }
    sink.receive(b); //直接调用输入流方法操作输入流缓冲区
}

/**
 * Receives a byte of data.  This method will block if no input is
 * available.
 * @param b the byte being received
 * @exception IOException If the pipe is <a href="#BROKEN"> <code>broken</code></a>,
 *          {@link #connect(java.io.PipedOutputStream) unconnected},
 *          closed, or if an I/O error occurs.
 * @since     JDK1.1
 */
protected synchronized void receive(int b) throws IOException {
    checkStateForReceive(); //检查可写入状态
    writeSide = Thread.currentThread(); //获取输入流线程
    if (in == out) //满,即缓冲区数据已读取完
        awaitSpace();
    if (in < 0) { //缓冲区为空
        in = 0;
        out = 0;
    }
    buffer[in++] = (byte)(b & 0xFF); //写入,限定为8位
    if (in >= buffer.length) { //
        in = 0;
    }
}

2.5 PipedInputStream read方法

/**
 * Reads the next byte of data from this piped input stream. The
 * value byte is returned as an <code>int</code> in the range
 * <code>0</code> to <code>255</code>.
 * This method blocks until input data is available, the end of the
 * stream is detected, or an exception is thrown.
 *
 * @return     the next byte of data, or <code>-1</code> if the end of the
 *             stream is reached.
 * @exception  IOException  if the pipe is
 *           {@link #connect(java.io.PipedOutputStream) unconnected},
 *           <a href="#BROKEN"> <code>broken</code></a>, closed,
 *           or if an I/O error occurs.
 */
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; //receive中将out置为0
    }

    return ret;
}

参考:

[1] http://www.cnblogs.com/skywang12345/p/io_04.html
[2] http://www.2cto.com/kf/201402/279143.html
[3] http://www.cnblogs.com/meng72ndsc/archive/2010/12/23/1915358.html

猜你喜欢

转载自blog.csdn.net/sk199048/article/details/51222719