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寫入bpublic void write(byte b[], int off, int len); //向output寫入位元組數組bpublic 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緩衝區寫入bsynchronized void receive(byte b[], int off, int len); //向input寫入位元組數組bpublic synchronized int read(); //讀取緩衝區下一個位元組public synchronized int read(byte b[], int off, int len) //讀取緩衝區位元組數組到bpublic 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.Testpublic 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