PipedOutputStream和PipedInputStream分别是管道输出流和管道输入流,它们都是线程安全的,作用是让多线程可以通过管道进行线程间的通讯。在使用管道通信时,必须将PipedOutputStream和PipedInputStream配套使用。
使用管道通信时,大致的流程是:我们在线程A中向PipedOutputStream中写入数据,这些数据会自动的发送到与PipedOutputStream对应的PipedInputStream中,进而存储在PipedInputStream的缓冲中;此时,线程B通过读取PipedInputStream中的数据。就可以实现线程A和线程B的通信。
Java官方不推荐在单个线程中同时使用管道输出流和管道输入流,这种用法可能会导致死锁。
Attempting to use both objects from a single thread is not recommended, as it may deadlock the thread.
PipedOutputStream
构造方法
PipedOutputStream() 构造一个管道输出流,它需要被连接到一个管道输入流
PipedOutputStream(PipedInputStream snk) 构造一个连接到管道输入流snk的管道输出流
void connect(PipedInputStream snk)
void write(int b)
void write(byte b[], int off, int len)
void flush()
void close()
源码解析:
PipedOutputStream方法都用synchronized关键字标识,因此是线程安全的。
PipedOutputStream内部不存储数据,所有的数据都直接写到对应连接的PipedInputStream。
void connect(PipedInputStream snk) 将管道输出流连接到管道输入流snk 。
void write(int b) 将int类型的byte数据b写入到管道输出流,实质是调用sink.receive方法接受b。
void write(byte b[], int off, int len) 将数组b中从下表off开始,长度为len的数据写入到管道输出流,实质是调用sink.receive方法接受对应的子数组。
void flush() 刷新管道输出流
PipedInputStream
构造方法
PipedInputStream() 定义一个默认管道输入流,调用initPipe方法定义底层存储数组size为1024
PipedInputStream(int pipeSize) 定义一个管道输入流,底层存储数组的size为pipeSize
PipedInputStream(PipedOutputStream src) 定义一个绑定到管道输出流src的管道输入流,底层存储数组size为1024
PipedInputStream(PipedOutputStream src, int pipeSize) 定义一个绑定到管道输出流src的管道输入流,四层存储数组size为pipeSize
void initPipe(int pipeSize) 将底层存储数组指向一个新数组,数组的size为pipeSize
void connect(PipedOutputStream src) 将字节输入流连接到字节输出流src
void receive(int b) 接受int类型的byte值b,如果当前读写队列是满的,则会阻塞直到有可用于写入的空间
void receive(byte b[], int off, int len) 将数组b从索引off开始的长度为len的数据写入管道输入流,如果当前读写空间不够,则会阻塞直到有足够的空间
int read() 从管道读出下一个数据
源码解析
PipedInputStream底层数据存储在缓冲数组byte buffer[]中,int in表示下一个数据被写入的位置,int out表示下一个数据被读出的位置,当in = out表示当前缓冲去已满。
PipedInputStream具有两个线程readSide和writeSide,readSide用于read()方法读取数据,writeSide用于读取数据,对于同一管道而言,只会有一个读线程和写线程,保证了线程安全。
public class PipeExample {
public static void main(String[] args){
PipeExample pipeExample=new PipeExample();
PipeExample.Sender sender=pipeExample.new Sender();
PipeExample.Receiver receiver=pipeExample.new Receiver();
PipedInputStream in=receiver.getInputStream();
PipedOutputStream out =sender.getOutputStream();
try {
in.connect(out);
sender.start();
receiver.start();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 管道输出流测试
*/
class Sender extends Thread{
//定义一个管道输出流,需要绑定到一个管道输入流才能正常工作
private PipedOutputStream out=new PipedOutputStream();
// 获得“管道输出流”对象
public PipedOutputStream getOutputStream(){
return out;
}
@Override
public void run() {
//writeShortMessage();
writeLongMessage();
}
/**
* 向管道输入流写入一个短字符串
*/
private void writeShortMessage(){
String info="Write a short message.";
try {
out.write(info.getBytes());
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 向管道输出流写入一个长字符串
*/
private void writeLongMessage(){
StringBuffer sb=new StringBuffer();
//写入1020个字符
for(int i=0;i<102;i++){
sb.append("0123456789");
}
//写入26个字符
sb.append("abcdefghijklmnopqrstuvwxyz");
//str的长度为1046
String str=sb.toString();
try {
out.write(str.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 管道输入流测试
*/
public class Receiver extends Thread{
private PipedInputStream in=new PipedInputStream();
// 获得“管道输入流”对象
public PipedInputStream getInputStream(){
return in;
}
@Override
public void run() {
//readMessageOnce();
//读取超过1024个字节的数据
readMessageContinued();
}
/**
* 从管道输入流中读取1024个字节的数据
*/
public void readMessageOnce(){
byte[] buf=new byte[2048];
try{
//将管道输入流的数据写入到byte数组,由于管道输入流默认只有1024字节,所以只会写入1024个字节的数据
int len=in.read(buf);
System.out.println(new String(buf,0,len));
in.close();
}catch(IOException e){
e.printStackTrace();
}
}
public void readMessageContinued(){
int total=0;
while(true) {
byte[] buf = new byte[1024];
try {
int len = in.read(buf);
total += len;
System.out.println(new String(buf,0,len));
// 若读取到1024+个字节后,结束循环
if (total > 1024)
break;
} catch (IOException e) {
e.printStackTrace();
}
}
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}