日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

Java中的管道流 PipedOutputStream和PipedInputStream

發布時間:2023/12/15 综合教程 36 生活家
生活随笔 收集整理的這篇文章主要介紹了 Java中的管道流 PipedOutputStream和PipedInputStream 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

我們在學習IO流的時候可能會學字節流、字符流等,但是關于管道流的相信大部分視頻或者教程都是一語帶過,第一個是因為這個東西在實際開發中用的也不是很多,但是學習無止境,存在既有理。JDK中既然有個類那說明他并不是一無是處,只是我們目前還沒有場景用到它,那說明我們說的還不夠,知識點還不足以去駕馭它。

管道流其實是一個很有魅力的流,用法也很獨特。他用來連接兩個線程之間的通信,比如傳輸文件等。它們的作用是讓多線程可以通過管道進行線程間的通訊。在使用管道通信時,必須將PipedOutputStream和PipedInputStream配套使用。費話不多說,我們來看一個例子:

public class PipdTest {

	public static void main(String[] args) throws IOException {

		// 創建一個發送者對象
		Sender sender = new Sender();
		// 創建一個接收者對象
		Receiver receiver = new Receiver();
		// 獲取輸出管道流
		PipedOutputStream outputStream = sender.getOutputStream();
		// 獲取輸入管道流
		PipedInputStream inputStream = receiver.getInputStream();
		// 鏈接兩個管道,這一步很重要,把輸入流和輸出流聯通起來
		outputStream.connect(inputStream);
		// 啟動發送者線程
		sender.start();
		// 啟動接收者線程
		receiver.start();
	}
}

/**
 * 發送線程
 * 
 * @author yuxuan
 *
 */
class Sender extends Thread {

	// 聲明一個 管道輸出流對象 作為發送方
	private PipedOutputStream outputStream = new PipedOutputStream();

	public PipedOutputStream getOutputStream() {
		return outputStream;
	}

	@Override
	public void run() {
		String msg = "Hello World";
		try {
			outputStream.write(msg.getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				// 關閉輸出流
				outputStream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

/**
 * 接收線程
 * 
 * @author yuxuan
 *
 */
class Receiver extends Thread {

	// 聲明一個 管道輸入對象 作為接收方
	private PipedInputStream inputStream = new PipedInputStream();

	public PipedInputStream getInputStream() {
		return inputStream;
	}

	@Override
	public void run() {
		byte[] buf = new byte[1024];
		try {
			// 通過read方法 讀取長度
			int len = inputStream.read(buf);
			System.out.println(new String(buf, 0, len));
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				// 關閉輸入流
				inputStream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

上面的代碼有幾個點需要掌握清楚。

1、第一個就是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;
/*代表連接該管道輸入流的輸出流PipedOutputStream下一個字節將存儲在循環緩沖數組buffer的位置。
當in<0說明緩沖數組是空的;當in==out說明緩沖數組已滿。*/
        snk.in = -1;
        //代表該管道輸入流下一個要讀取的字節在循環緩沖數組中的位置
        snk.out = 0;
         //表示該管道輸入流是否與管道輸出流建立了連接,true為已連接
        snk.connected = true;
    }

我們可以看到,他是一個線程同步的方法,通過synchronized 關鍵字修飾。

除了調用connect方法之外,還可以在構造函數中直接傳進去,源碼如下:

當然管道流也有一些注意事項:

管道流僅用于多個線程之間傳遞信息,若用在同一個線程中可能會造成死鎖;
管道流的輸入輸出是成對的,一個輸出流只能對應一個輸入流,使用構造函數或者connect函數進行連接;
一對管道流包含一個緩沖區,其默認值為1024個字節,若要改變緩沖區大小,可以使用帶有參數的構造函數;
管道的讀寫操作是互相阻塞的,當緩沖區為空時,讀操作阻塞;當緩沖區滿時,寫操作阻塞;
管道依附于線程,因此若線程結束,則雖然管道流對象還在,仍然會報錯“read dead end”;
管道流的讀取方法與普通流不同,只有輸出流正確close時,輸出流才能讀到-1值。

下面我們來看write方法的源碼:

看到這里是不是一目了然了。以下還有一些注意事項,我們來看:

PipedInputStream運用的是一個1024字節固定大小的循環緩沖區。寫入PipedOutputStream的數據實際上保存到對應的 PipedInputStream的內部緩沖區。從PipedInputStream執行讀操作時,讀取的數據實際上來自這個內部緩沖區。如果對應的 PipedInputStream輸入緩沖區已滿,任何企圖寫入PipedOutputStream的線程都將被阻塞。而且這個寫操作線程將一直阻塞,直至出現讀取PipedInputStream的操作從緩沖區刪除數據。

這意味著,向PipedOutputStream寫數據的線程不應該是負責從對應PipedInputStream讀取數據的唯一線程。從圖二可以清楚地看出這里的問題所在:假設線程t是負責從PipedInputStream讀取數據的唯一線程;另外,假定t企圖在一次對 PipedOutputStream的write()方法的調用中向對應的PipedOutputStream寫入2000字節的數據。在t線程阻塞之前,它最多能夠寫入1024字節的數據(PipedInputStream內部緩沖區的大小)。然而,一旦t被阻塞,讀取 PipedInputStream的操作就再也不會出現,因為t是唯一讀取PipedInputStream的線程。這樣,t線程已經完全被阻塞,同時,所有其他試圖向PipedOutputStream寫入數據的線程也將遇到同樣的情形。這并不意味著在一次write()調用中不能寫入多于1024字節的數據。但應當保證,在寫入數據的同時,有另一個線程從PipedInputStream讀取數據。

從PipedInputStream讀取數據時,如果符合下面三個條件,就會出現IOException異常:

試圖從PipedInputStream讀取數據,
PipedInputStream的緩沖區為“空”(即不存在可讀取的數據),
最后一個向PipedOutputStream寫數據的線程不再活動(通過Thread.isAlive()檢測)。

這是一個很微妙的時刻,同時也是一個極其重要的時刻。假定有一個線程w向PipedOutputStream寫入數據;另一個線程r從對應的 PipedInputStream讀取數據。下面一系列的事件將導致r線程在試圖讀取PipedInputStream時遇到IOException異常:

w向PipedOutputStream寫入數據。
w結束(w.isAlive()返回false)。
r從PipedInputStream讀取w寫入的數據,清空PipedInputStream的緩沖區。
r試圖再次從PipedInputStream讀取數據。這時PipedInputStream的緩沖區已經為空,而且w已經結束,從而導致在讀操作執行時出現IOException異常。

如果一個寫操作在PipedOutputStream上執行,同時最近從對應PipedInputStream讀取的線程已經不再活動(通過 Thread.isAlive()檢測),則寫操作將拋出一個IOException異常。假定有兩個線程w和r,w向 PipedOutputStream寫入數據,而r則從對應的PipedInputStream讀取。下面一系列的事件將導致w線程在試圖寫入 PipedOutputStream時遇到IOException異常:

寫操作線程w已經創建,但r線程還不存在。
w向PipedOutputStream寫入數據。
讀線程r被創建,并從PipedInputStream讀取數據。
r線程結束。
w企圖向PipedOutputStream寫入數據,發現r已經結束,拋出IOException異常。

此篇文章主要用于理解運用管道流,如果在實際項目開發中用到的話建議一定要研究透在用,他的坑可不止我上面諾列的這些哦

有問題可以在下面評論,技術問題可以私聊我

總結

以上是生活随笔為你收集整理的Java中的管道流 PipedOutputStream和PipedInputStream的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。