天天看点

线程间通信之管道流(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)
           

继续阅读