一 Java IO管道概述
Java IO中的管道流主要是为运行在同一个JVM中的两个线程提供通信能力,
所以管道也可以作为数据源和目标媒介。
注意,不同的JVM中的线程是不能进行通信的(不同进程)。
基于字节流的管道流:
PipedInputStream和PipedOutputStream,可以通过这两个流创建字节流管道;
一个管道输入流(PipedInputStream)应该与一个管道输出流(PipedOutputStream)相关联。
一个线程通过管道输出流(PipedOutputStream)写入的数据可以被另一个线程通过
相关联的管道输入流(PipedInputStream)读取出来。
PipedReader和PipedWriter,可以通过这两个流创建字符流管道;
处理方式类似于字符流。
如果使用同一个线程处理两个相关联的管道流时,read()方法和write()方法调用时会导致流阻塞,
可能会导致线程死锁。所以,在使用管道流时,务必将它们分配给不同的线程。
注意一下,PipedInputStream运用的是一个1024字节固定大小的循环缓冲区,
写入PipedOutputStream的数据实际上保存到了对应的PipedInputStream的内部缓冲区。
PipedInputStream执行读操作时,读取的数据实际上来自这个内部缓冲区。如果对应的PipedInputStream输入缓冲区已满,任何企图写入PipedOutputStream的线程都将被阻塞。
而且这个写操作线程将一直阻塞,直至出现读取PipedInputStream的操作从缓冲区删除数据。
这就是为什么不要用同一个线程来处理两个关联管道流的原因。
管道流向流程图:
PipedOutputStream从内存输出数据写入到PipedInputStream的缓冲区,PipedInputStream从PipedInputStream缓冲区读取管道流数据。
创建一个线程通过管道输出流(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();
}
}
}
}
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();
}
}
}
}
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();
}
}
}
运行结果:
到这里,一个线程通过管道输出流写入数据,另外一个线程通过管道输入流读取数据,
从而完美的通过管道流实现了线程之间的数据,是不是感觉自己很屌,哈哈。
线程之间的通信,除了使用管道流进行通信之外,其实一个JVM中不同线程之间还有许多其他的通信方式。
在实际应用中,线程之间交互一般传递的是完整的对象,很少有哪种传递原始的字节数据这种神操作,
但是,如果你需要在线程之间传递字节数据,使用Java IO的管道流是一个不错的选择。