在Java中提供了各种各样的输入输出流stream,我们可以用他们对数据进行方便的操作。其中,管道(pipeStream)是一种特殊的流,它可以用于在不同的线程之间传送数据。一个线程将数据输出到管道中,另一个线程从管道中读取需要的数据,实现不同线程之间的通信而无需通过临时文件。管道通信可以达到解耦的目的,产生数据的线程不需要直接调用处理数据的方法并等待返回结果,只需要将数据放入管道,接着继续执行自己的任务;而处理数据的线程直接从管道中拿出数据进行处理,不需要进行轮询来获取数据。
Java的JDK中提供了四个类使得线程之间可以进行通信:
1)PipedInputStream和PipedOutputStream
2)PipedReader和PipedWriter
下面用一个例子来实现线程间通过管道使用字节流进行通信:
//接受一个输出流参数,向输出流中写入数据 public class WriteData { public void writeData(PipedOutputStream out) { try { System.out.println("write:"); for(int i=0;i<100;i++) { String outData = ""+(i+1); out.write(outData.getBytes()); System.out.print(outData); } System.out.println(); out.close(); }catch(IOException e) { e.printStackTrace(); } } } //接受一个输入流参数,读取数据 public class ReadData { public void readData(PipedInputStream input) { try { System.out.println("read:"); byte[] byteArray = new byte[20]; int readLength = input.read(byteArray); while(readLength!=-1) { String newData = new String(byteArray, 0, readLength); System.out.print(newData); readLength = input.read(byteArray); } System.out.println(); input.close(); }catch(IOException e) { e.printStackTrace(); } } } //输出流线程 public class Thw extends Thread{ private WriteData write; private PipedOutputStream out; public Thw(WriteData write, PipedOutputStream out) { super(); this.write = write; this.out = out; } @Override public void run() { write.writeData(out); } } //输入流线程 public class Thr extends Thread{ private ReadData read; private PipedInputStream input; public Thr(ReadData read, PipedInputStream input) { super(); this.read = read; this.input = input; } @Override public void run() { read.readData(input); } } public class Run { public static void main(String args[]) { try { WriteData write = new WriteData(); ReadData read = new ReadData(); PipedOutputStream out = new PipedOutputStream(); PipedInputStream input = new PipedInputStream(); out.connect(input); Thw thw = new Thw(write, out); Thr thr = new Thr(read, input); thr.start(); Thread.sleep(1000); thw.start(); }catch(IOException e) { e.printStackTrace(); }catch(InterruptedException ex) { ex.printStackTrace(); } } }
运行以上程序可以看到如下运行结果:
/*
read:
write:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
*/
这里解释一下ReadData类中,readData方法的执行过程。首先创建一个长度为20的byte数组,并用input.read(byteArray)的返回结果初始化变量readLength,read(byteArray)默认返回read(byteArray,0,byteArray.length)的结果,其中三个参数分别表示,要将输入流中数据写入的目标数组,写入目标数组的起始下标,以及写入的长度。read()的源码如下:
public int read(byte b[], int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int c = read(); if (c == -1) { return -1; } b[off] = (byte)c; int i = 1; try { for (; i < len ; i++) { c = read(); if (c == -1) { break; } b[off + i] = (byte)c; } } catch (IOException ee) { } return i; }可以看到,在jdk官方文档中有说明:
If no byte is available because the stream is at the end of the file, the value -1 is returned;
当输入流到达末尾时,会返回-1,且当readLength==-1时停止读取数据并关闭输入流。在主函数中,我们首先创建一个read线程,负责从输入流中读取数据并打印,然后启动。当执行到int readLength = input.read(byteArray);时,因为write线程还没有启动,所以没用数据写入,read线程被阻塞,直到有数据写入才继续向下运行。