Java IO深入理解管道(PipedInputStream、PipedOutputStream、PipedReader、PipedWriter)

一 Java IO管道概述

Java IO中的管道流主要是为运行在同一个JVM中的两个线程提供通信能力,

所以管道也可以作为数据源和目标媒介。

注意,不同的JVM中的线程是不能进行通信的(不同进程)。


二 Java IO创建管道

基于字节流的管道流: 

PipedInputStream和PipedOutputStream,可以通过这两个流创建字节流管道;

一个管道输入流(PipedInputStream)应该与一个管道输出流(PipedOutputStream)相关联。

一个线程通过管道输出流(PipedOutputStream)写入的数据可以被另一个线程通过

相关联的管道输入流(PipedInputStream)读取出来。


基于字符流的管道流: 

PipedReader和PipedWriter,可以通过这两个流创建字符流管道;

处理方式类似于字符流。


三 管道和线程

如果使用同一个线程处理两个相关联的管道流时,read()方法和write()方法调用时会导致流阻塞,

可能会导致线程死锁。所以,在使用管道流时,务必将它们分配给不同的线程。

注意一下,PipedInputStream运用的是一个1024字节固定大小的循环缓冲区,

写入PipedOutputStream的数据实际上保存到了对应的PipedInputStream的内部缓冲区。

PipedInputStream执行读操作时,读取的数据实际上来自这个内部缓冲区。

如果对应的PipedInputStream输入缓冲区已满,任何企图写入PipedOutputStream的线程都将被阻塞。

而且这个写操作线程将一直阻塞,直至出现读取PipedInputStream的操作从缓冲区删除数据。

这就是为什么不要用同一个线程来处理两个关联管道流的原因。

管道流向流程图:

PipedOutputStream从内存输出数据写入到PipedInputStream的缓冲区,PipedInputStream从PipedInputStream缓冲区读取管道流数据。

Java IO深入理解管道(PipedInputStream、PipedOutputStream、PipedReader、PipedWriter)_第1张图片



四 Java IO管道实例

创建一个线程通过管道输出流(PipedOutputStream)向管道写入数据,

另外一个线程通过管道输入流(PipedInputStream)读取数据。

让两个线程通过管道流进行数据的交互。

WriteThread:

package com.lanhuigu.io.pipes;

import java.io.IOException;
import java.io.PipedOutputStream;

/**
 * 通过管道输出流(PipedOutputStream)向管道输入流(PipedInputStream)的缓冲区写入数据。
 */
public class WriteThread implements Runnable{
    // 管道输出流
    private PipedOutputStream output;

    public WriteThread(PipedOutputStream output) {
        this.output = output;
    }

    @Override
    public void run() {
        String str = "hello, piped!";
        try {
            // 向管道输入流(PipedInputStream)的缓冲区写入数据
            output.write(str.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // 关闭管道输出流
                output.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

ReadThread:
package com.lanhuigu.io.pipes;

import java.io.IOException;
import java.io.PipedInputStream;
/**
 * 通过管道输入流(PipedInputStream)从管道输出流(PipedInputStream)的缓存区读取数据。
 */
public class ReadThread implements Runnable{
    // 管道输入流
    private PipedInputStream input;

    public ReadThread(PipedInputStream input) {
        this.input = input;
    }

    @Override
    public void run() {
        try {
            // 创建字节数组
            byte[] data = new byte[1024];
            // 将管道输出流内容放入字节数组中
            input.read(data);
            // 字节数组转换为字符串
            String str = new String(data);
            // 打印读取到的管道流内容
            System.out.println("读取到的管道流内容: " + str);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // 关闭管道输入流
                input.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

PipedTest:
package com.lanhuigu.io.pipes;

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

/**
 * 管道流测试类,通过WriteThread线程写入数据,ReadThread线程读取数据并打印到控制台。
 */
public class PipedTest {

    public static void main(String[] args) {
        try {
            /** 管道输出流和输入流定义 */
            PipedOutputStream output = new PipedOutputStream();
            PipedInputStream input = new PipedInputStream();

            /** 写线程和读线程创建 */
            WriteThread writeThread = new WriteThread(output);
            ReadThread readThread = new ReadThread(input);

            Thread tWrite = new Thread(writeThread);
            Thread tRead = new Thread(readThread);

            /** 管道输入流和输出流通过connect方法建立关联关系 */
            output.connect(input);

            /** 通过start启动线程 */
            tWrite.start();
            tRead.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

运行结果:

Java IO深入理解管道(PipedInputStream、PipedOutputStream、PipedReader、PipedWriter)_第2张图片

到这里,一个线程通过管道输出流写入数据,另外一个线程通过管道输入流读取数据,

从而完美的通过管道流实现了线程之间的数据,是不是感觉自己很屌,哈哈。


五 线程通信管道的替代

线程之间的通信,除了使用管道流进行通信之外,其实一个JVM中不同线程之间还有许多其他的通信方式。

在实际应用中,线程之间交互一般传递的是完整的对象,很少有哪种传递原始的字节数据这种神操作,

但是,如果你需要在线程之间传递字节数据,使用Java IO的管道流是一个不错的选择。


你可能感兴趣的:(#,---IO)