pipe管道作爲線程之間通信的一種方式
- 首先作爲對比我們先了解下再BIO模式下的pipe的使用 Pipe爲運行在同一個JVM中的兩個線程提供了通信的能力,注意是同一個jvm上,如果在不同的jvm就是不同的進程了,pipe就無能爲力了。
- Java IO中管道的創建
Java IO中的PipedOutputStream和PipedInputStream創建管道。PipedInputStream流應該和PipedOutputStream流相關聯。一個線程通過PipedOutputStream寫入的數據可以被另一個線程通過相關聯的PipedInputStream讀取出來。
- Java IO 管道的使用
read()方法和write()方法,分別是寫入數據和讀取數據,但是需要注意的是 這兩個方法調用時會導致流阻塞,就是說如果嘗試在一個線程中同時進行讀和寫,可能會導致線程死鎖。
- 管道輸出流和管道輸入流之間建立連接
PipedOutputStream outputPipe = new PipedOutputStream(); //構造器方式 PipedInputStream inputPipe = new PipedInputStream(output); 或者使用兩者共有的方法connect()方法連接
4 完整示例如下:
public static void main(String[] args) throws IOException { //分別創建輸入和輸出的管道流 final PipedOutputStream outputPipe = new PipedOutputStream(); //PipedInputStream(output);構造器將管道數據流和管道輸入流建立通信 final PipedInputStream inputPipe = new PipedInputStream(output); //在第一個線程將數據寫入管道輸出流 Thread thread1 = new Thread(new Runnable() { @Override public void run() { try { outputPipe.write("Hello world, pipe!".getBytes()); } catch (IOException e) { } } }); //第二個線程從管道輸入流中讀取數據 Thread thread2 = new Thread(new Runnable() { @Override public void run() { try { int data = input.read(); while(data != -1){ System.out.print((char) data); data = inputPipe .read(); } } catch (IOException e) { } } }); thread1.start(); thread2.start(); }
- 下面爲NIO模式下的管道使用 管道pipe和線程之間的關係如下圖:
圖片.png
由此可以看出NIO的管道和BIO模式下的管道是不同的,在NIO模式下沒有輸入輸出流的概念但是使用發送sink和讀取source的channe。使用同一個pipe實現線程之間數據的流轉
- 創建pipe管道
//多個線程之間使用同一個管道 Pipe pipe = Pipe.open();
- 向管道寫數據。先獲取sinkChannel ,然後將數據寫入sinkChannel
//1、先獲取sinkChannel Pipe.SinkChannel sinkChannel = pipe.sink(); //2、將數據寫入sinkChannel String newData = "New String to write to file..." + System.currentTimeMillis(); ByteBuffer buf = ByteBuffer.allocate(48); buf.clear(); buf.put(newData.getBytes()); buf.flip(); while(buf.hasRemaining()) { sinkChannel.write(buf); }
- 從管道中讀取數據。先獲取sourceChannel ,然後從sourceChannel 讀取數據
//1、先獲取sourceChannel Pipe.SourceChannel sourceChannel = pipe.source(); //2、然後從sourceChannel 讀取數據 ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf);
2、3兩步的讀寫分別在不同的線程實現
- 完整實例:
public static void pipeExample(){ Pipe pipe = null; ExecutorService exec = Executors.newFixedThreadPool(2); try{ //創建pipe pipe = Pipe.open(); final Pipe pipeTemp = pipe; //線程一向管道中寫入數據 exec.submit(new Callable<Object>(){ @Override public Object call() throws Exception { Pipe.SinkChannel sinkChannel = pipeTemp.sink();//向通道中寫數據 while(true){ TimeUnit.SECONDS.sleep(1); String newData = "hello world"+System.currentTimeMillis(); ByteBuffer buf = ByteBuffer.allocate(1024); buf.clear(); buf.put(newData.getBytes()); //反轉後可讀 buf.flip(); while(buf.hasRemaining()){ System.out.println(buf); sinkChannel.write(buf); } } } }); //線程二讀取管道中的數據 exec.submit(new Callable<Object>(){ @Override public Object call() throws Exception { Pipe.SourceChannel sourceChannel = pipeTemp.source();//向通道中讀數據 while(true){ TimeUnit.SECONDS.sleep(1); ByteBuffer buf = ByteBuffer.allocate(1024); buf.clear(); //可讀大小 int bytesRead = sourceChannel.read(buf); System.out.println("bytesRead="+bytesRead); while(bytesRead >0 ){ buf.flip(); byte b[] = new byte[bytesRead]; int i=0; while(buf.hasRemaining()){ b[i]=buf.get(); System.out.printf("%X",b[i]); i++; } String s = new String(b); System.out.println("========>>>>>>"+s); //無數據時跳出當前循環體 bytesRead = sourceChannel.read(buf); } } } }); }catch(IOException e){ e.printStackTrace(); }finally{ exec.shutdown(); } }