天天看點

線程間通信之管道流(PipedInputStream、PipedOutputStream)

下面是用檔案輸入輸出流配合管道輸入輸出流完成檔案複制的功能。

發送方:檔案輸入流讀資料到管道輸出流。

接收方:管道輸入流讀資料到檔案輸出流。

package pipedCommunication;

import java.io.*;

public class Demo {

    //發送線程
    static class Sender implements Runnable {
        PipedOutputStream pipedOutputStream = null;

        public Sender(PipedOutputStream pipedOutputStream) {
            this.pipedOutputStream = pipedOutputStream;
        }

        @Override
        public void run() {
            int len;
            byte[] b = new byte[4];
            try (BufferedInputStream bis = new BufferedInputStream(
                    new FileInputStream("C:\\Users\\PUBG\\Desktop\\test.txt"));) {  //這裡可以改為從控制台讀入
                while ((len = bis.read(b)) != -1) {
                    pipedOutputStream.write(b, 0, len);
                }
                pipedOutputStream.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //接收線程
    static class Receiver implements Runnable {
        PipedInputStream pipedInputStream = null;

        public Receiver(PipedInputStream pipedInputStream) {
            this.pipedInputStream = pipedInputStream;
        }

        @Override
        public void run() {
            File file = new File("C:\\Users\\PUBG\\Desktop\\test1.txt");
            if (file.exists())
                file.delete();
            byte[] b = new byte[6];
            int len;
            try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file))) {
                while ((len = pipedInputStream.read(b)) != -1) { //流中不存在資料時會阻塞
                    byte[] b1 = new byte[len];//拷貝一份一次讀取的資料
                    for (int i = 0; i < len; i++) {
                        b1[i] = b[i];
                    }
                    System.out.println("寫入檔案: " + new String(b1, "GBK"));
                    bos.write(b, 0, len);
                }
                pipedInputStream.close();
                System.out.println("寫入完畢");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            ;
        }
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        PipedInputStream pipedInputStream = new PipedInputStream();
        PipedOutputStream pipedOutputStream = new PipedOutputStream();
        pipedOutputStream.connect(pipedInputStream);// 也可以在構造方法裡連接配接PipedOutputStream pipedOutputStream=new PipedOutputStream(pipedInputStream);

        Thread receiver = new Thread(new Receiver(pipedInputStream));
        Thread sender = new Thread(new Sender(pipedOutputStream));

        receiver.start();
        sender.start();

    }
}
           
運作結果:
           
線程間通信之管道流(PipedInputStream、PipedOutputStream)

在中文系統中記事本預設編碼為GBK,以預設編碼儲存檔案開頭則無BOM标志。由于在記事本中全部為漢字,一個漢字在GBK編碼中占兩個位元組,而在程式中發送和接收都是2的倍數,則每次讀取的資料都能轉為漢字顯示在控制台上。

在PipedInputStream緩沖區中無資料時,read()方法會阻塞,有資料才繼續讀,是以先啟動接收者線程後啟動發送者線程,程式一樣正常執行。

如果在發送線程中沒有關閉pipedOutputStream,在發送線程退出後,接收線程的pipedInputStream.read()方法會抛出異常,記得用完後關閉。

java.io.IOException: Write end dead
at java.io.PipedInputStream.read(PipedInputStream.java:310)
at java.io.PipedInputStream.read(PipedInputStream.java:377)
at java.io.InputStream.read(InputStream.java:101)
at pipedCommunication.Demo$Receiver.run(Demo.java:45)
at java.lang.Thread.run(Thread.java:748)
           

繼續閱讀