【轉】http://blog.csdn.net/javatiger427/article/details/5357827java
1、Java管道流
要在文本框中顯示控制檯輸出,咱們必須用某種方法「截取」控制檯流。換句話說,咱們要有一種高效地讀取寫入到System.out和System.err 全部內容的方法。若是你熟悉Java的管道流PipedInputStream和PipedOutputStream,就會相信咱們已經擁有最有效的工具。
寫入到PipedOutputStream輸出流的數據能夠從對應的PipedInputStream輸入流讀取。Java的管道流極大地方便了咱們截取控制檯輸出。Listing 1顯示了一種很是簡單的截取控制檯輸出方案。
【Listing 1:用管道流截取控制檯輸出】
PipedInputStream pipedIS = new PipedInputStream();
PipedOutputStream pipedOS = new PipedOutputStream();
try {
pipedOS.connect(pipedIS);
}
catch(IOException e) {
System.err.println("鏈接失敗");
System.exit(1);
}
PrintStream ps = new PrintStream(pipedOS);
System.setOut(ps);
System.setErr(ps);
能夠看到,這裏的代碼極其簡單。咱們只是創建了一個PipedInputStream,把它設置爲全部寫入控制檯流的數據的最終目的地。全部寫入到控制檯流的數據都被轉到PipedOutputStream,這樣,從相應的PipedInputStream讀取就能夠迅速地截獲全部寫入控制檯流的數據。接下來的事情彷佛只剩下在Swing JTextArea中顯示從pipedIS流讀取的數據,獲得一個可以在文本框中顯示控制檯輸出的程序。遺憾的是,在使用Java管道流時有一些重要的注意事項。只有認真對待全部這些注意事項才能保證Listing 1的代碼穩定地運行。下面咱們來看第一個注意事項。
1.1 注意事項一
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讀取數據。
Listing 2示範了這個問題。這個程序用一個線程交替地讀取PipedInputStream和寫入PipedOutputStream。每次調用write()向 PipedInputStream的緩衝區寫入20字節,每次調用read()只從緩衝區讀取並刪除10個字節。內部緩衝區最終會被寫滿,致使寫操做阻塞。因爲咱們用同一個線程執行讀、寫操做,一旦寫操做被阻塞,就不能再從PipedInputStream讀取數據。
【Listing 2:用同一個線程執行讀/寫操做致使線程阻塞】
import java.io.*;
public class Listing2 {
static PipedInputStream pipedIS = new PipedInputStream();
static PipedOutputStream pipedOS =
new PipedOutputStream();
public static void main(String[] a){
try {
pipedIS.connect(pipedOS);
}
catch(IOException e) {
System.err.println("鏈接失敗");
System.exit(1);
}
byte[] inArray = new byte[10];
byte[] outArray = new byte[20];
int bytesRead = 0;
try {
// 向pipedOS發送20字節數據
pipedOS.write(outArray, 0, 20);
System.out.println(" 已發送20字節...");
// 在每一次循環迭代中,讀入10字節
// 發送20字節
bytesRead = pipedIS.read(inArray, 0, 10);
int i=0;
while(bytesRead != -1) {
pipedOS.write(outArray, 0, 20);
System.out.println(" 已發送20字節..."+i);
i++;
bytesRead = pipedIS.read(inArray, 0, 10);
}
}
catch(IOException e) {
System.err.println("讀取pipedIS時出現錯誤: " + e);
System.exit(1);
}
} // main()
}
只要把讀/寫操做分開到不一樣的線程,Listing 2的問題就能夠輕鬆地解決。Listing 3是Listing 2通過修改後的版本,它在一個單獨的線程中執行寫入PipedOutputStream的操做(和讀取線程不一樣的線程)。爲證實一次寫入的數據能夠超過 1024字節,咱們讓寫操做線程每次調用PipedOutputStream的write()方法時寫入2000字節。那麼,在 startWriterThread()方法中建立的線程是否會阻塞呢?按照Java運行時線程調度機制,它固然會阻塞。寫操做在阻塞以前實際上最多隻能寫入1024字節的有效載荷(即PipedInputStream緩衝區的大小)。但這並不會成爲問題,由於主線程(main)很快就會從 PipedInputStream的循環緩衝區讀取數據,空出緩衝區空間。最終,寫操做線程會從上一次停止的地方從新開始,寫入2000字節有效載荷中的剩餘部分。
【Listing 3:把讀/寫操做分開到不一樣的線程】
import java.io.*;
public class Listing3 {
static PipedInputStream pipedIS =
new PipedInputStream();
static PipedOutputStream pipedOS =
new PipedOutputStream();
public static void main(String[] args) {
try {
pipedIS.connect(pipedOS);
}
catch(IOException e) {
System.err.println("鏈接失敗");
System.exit(1);
}
byte[] inArray = new byte[10];
int bytesRead = 0;
// 啓動寫操做線程
startWriterThread();
try {
bytesRead = pipedIS.read(inArray, 0, 10);
while(bytesRead != -1) {
System.out.println("已經讀取" +
bytesRead + "字節...");
bytesRead = pipedIS.read(inArray, 0, 10);
}
}
catch(IOException e) {
System.err.println("讀取輸入錯誤.");
System.exit(1);
}
} // main()
// 建立一個獨立的線程
// 執行寫入PipedOutputStream的操做
private static void startWriterThread() {
ne工具