管道(PipedOutputStream和PipedInputStream)的簡介,源碼分析和樣本
本章,我們對java 管道進行學習。
java 管道介紹
在java中,PipedOutputStream和PipedInputStream分別是管道輸出資料流和管道輸入資料流。
它們的作用是讓多線程可以通過管道進行線程間的通訊。在使用管道通訊時,必須將PipedOutputStream和PipedInputStream配套使用。
使用管道通訊時,大致的流程是:我們線上程A中向PipedOutputStream中寫入資料,這些資料會自動的發送到與PipedOutputStream對應的PipedInputStream中,進而儲存在PipedInputStream的緩衝中;此時,線程B通過讀取PipedInputStream中的資料。就可以實現,線程A和線程B的通訊。
PipedOutputStream和PipedInputStream源碼分析
下面介紹PipedOutputStream和PipedInputStream的源碼。在閱讀它們的源碼之前,建議先看看源碼後面的樣本。待理解管道的作用和用法之後,再看源碼,可能更容易理解。
此外,由於在“java io系列03之 ByteArrayOutputStream的簡介,源碼分析和樣本(包括OutputStream)”中已經對PipedOutputStream的父類OutputStream進行了介紹,這裡就不再介紹OutputStream。
在“java io系列02之 ByteArrayInputStream的簡介,源碼分析和樣本(包括InputStream)”中已經對PipedInputStream的父類InputStream進行了介紹,這裡也不再介紹InputStream。
1. PipedOutputStream 源碼分析(基於jdk1.7.40)
package java.io; import java.io.*; public class PipedOutputStream extends OutputStream { // 與PipedOutputStream通訊的PipedInputStream對象 private PipedInputStream sink; // 建構函式,指定配對的PipedInputStream 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; // 初始化“管道輸入資料流”的讀寫位置 // int是PipedInputStream中定義的,代表“管道輸入資料流”的讀寫位置 snk.in = -1; // 初始化“管道輸出資料流”的讀寫位置。 // out是PipedInputStream中定義的,代表“管道輸出資料流”的讀寫位置 snk.out = 0; // 設定“管道輸入資料流”和“管道輸出資料流”為已串連狀態 // connected是PipedInputStream中定義的,用於表示“管道輸入資料流與管道輸出資料流”是否已經串連 snk.connected = true; } // 將int類型b寫入“管道輸出資料流”中。 // 將b寫入“管道輸出資料流”之後,它會將b傳輸給“管道輸入資料流” public void write(int b) throws IOException { if (sink == null) { throw new IOException("Pipe not connected"); } sink.receive(b); } // 將位元組數組b寫入“管道輸出資料流”中。 // 將數組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); } // 清空“管道輸出資料流”。 // 這裡會調用“管道輸入資料流”的notifyAll(); // 目的是讓“管道輸入資料流”放棄對當前資源的佔有,讓其它的等待線程(等待讀取管道輸出資料流的線程)讀取“管道輸出資料流”的值。 public synchronized void flush() throws IOException { if (sink != null) { synchronized (sink) { sink.notifyAll(); } } } // 關閉“管道輸出資料流”。 // 關閉之後,會調用receivedLast()通知“管道輸入資料流”它已經關閉。 public void close() throws IOException { if (sink != null) { sink.receivedLast(); } }}