Java NIO 進程間通信
轉自:http://blog.csdn.net/lingzhm/article/details/45026119
傳統的進程間通信的方式有大致如下幾種:
(1) 管道(PIPE)
(2) 命名管道(FIFO)
(3) 信號量(Semphore)
(4) 消息隊列(MessageQueue)
(5) 共享內存(SharedMemory)
(6) Socket
Java如何支持進程間通信。我們把Java進程理解為JVM進程。很明顯,傳統的這些大部分技術是無法被我們的應用程序利用了(這些進程間通信都是靠系統調用來實現的)。但是Java也有很多方法可以進行進程間通信的。
除了上面提到的Socket之外,當然首選的IPC可以使用Rmi,或者Corba也可以。另外Java nio的MappedByteBuffer也可以通過內存映射文件來實現進程間通信(共享內存)。
Java進程間通信可以采用的辦法:
Socket/RMI/WEBService/WebServer, 這些都可以實現直接的數據交換
Database/File, 這些可以實現間接的數據交換
看你的業務是否要求實時, 如果不需要, 用數據庫交換比較簡單
線程間通信:
可以直接傳入共享的變量來實現。
一看到 java NIO 的內存映射文件(MappedByteBuffer),讓我立即就聯想到 Windows 系統的內存映射文件。Windows 系統的內存映射文件能用來在多個進程間共享數據,即進程間的共享內存,是通過把同一塊內存區域映射到不同進程的地址空間中,從而達到共享內存。
Java NIO 的內存映射文件和 Windows 系統下的一樣,都能把物理文件的內容映射到內存中,那麽 MappedByteBuffer 是否能用來在不同 Java 進程(JVM) 間共享數據呢?答案是肯定的,這樣在通常的 Socket 方式來實現 Java 進程間通信之上又多了一種方法。
在 Windows 中內存映射文件可以是脫離物理文件而存在的一塊命名的內存區域,使用相同的內存映射名就能在不同的進程中共享同一片內存。然後,Java 的 MappedByteBuffer 總是與某個物理文件相關的,因為不管你是從 FileInputStream、FileOutputStream 還是 RandomAccessFile 得來的 FileChannel,再 map() 得到的內存映射文件 MappedByteBuffer,如果在構造 FileInputStream、FileOutputStream、RandomAccessFile 對象時不指定物理文件便會有 FileNotFoundException 異常。
所以 Java NIO 來實現共享內存的辦法就是讓不同進程的內存映射文件關聯到同一個物理文件,因為 MappedByteBuffer 能讓內存與文件即時的同步內容。嚴格說來,稱之為內存共享是不準確的,其實就是兩個 Java 進程通過中間文件來交換數據,用中間文件使得兩個進程的兩塊內存區域的內容得到及時的同步。
用圖來理解 Java NIO 的“共享內存”的實現原理:
知道了實現原理之後,下面用代碼來演示兩個進程間用內存映射文件來進行數據通信。代碼 WriteShareMemory.java 往映射文件中依次寫入 A、B、C ... Z,ReadShareMemory.java 逐個讀出來,打印到屏幕上。代碼對交換文件 swap.mm 的第一個字節作了讀寫標誌,分別是 0-可讀,1-正在寫,2-可讀。RandomAccessFile 得到的 Channel 能夠靈活的進行讀或寫,並且不會破壞原有文件內容,而 FileInputStream 或 FileOutputStream 取得的 Channel 則很難達到這一功效,所以使用了 RandomAccessFile 來獲得 FileChannel。
WriteShareMemory.java
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
package com.unmi;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
/**
* 往 "共享內存" 寫入數據
* @author Unmi
*/
public class WriteShareMemory {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
RandomAccessFile raf = new RandomAccessFile( "c:/swap.mm" , "rw" );
FileChannel fc = raf.getChannel();
MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0 , 1024 );
//清除文件內容
for ( int i= 0 ;i< 1024 ;i++){
mbb.put(i,( byte ) 0 );
}
//從文件的第二個字節開始,依次寫入 A-Z 字母,第一個字節指明了當前操作的位置
for ( int i= 65 ;i< 91 ;i++){
int index = i- 63 ;
int flag = mbb.get( 0 ); //可讀標置第一個字節為 0
if (flag != 0 ){ //不是可寫標示 0,則重復循環,等待
i --;
continue ;
}
mbb.put( 0 ,( byte ) 1 ); //正在寫數據,標誌第一個字節為 1
mbb.put( 1 ,( byte )(index)); //寫數據的位置
System.out.println( "程序 WriteShareMemory:" +System.currentTimeMillis() +
":位置:" + index + " 寫入數據:" + ( char )i);
mbb.put(index,( byte )i); //index 位置寫入數據
mbb.put( 0 ,( byte ) 2 ); //置可讀數據標誌第一個字節為 2
Thread.sleep( 513 );
}
}
}
|
ReadShareMemory.java
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
package com.unmi;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
/**
* 從 "共享內存" 讀出數據
* @author Unmi
*/
public class ReadShareMemory {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
RandomAccessFile raf = new RandomAccessFile( "c:/swap.mm" , "rw" );
FileChannel fc = raf.getChannel();
MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0 , 1024 );
int lastIndex = 0 ;
for ( int i= 1 ;i< 27 ;i++){
int flag = mbb.get( 0 ); //取讀寫數據的標誌
int index = mbb.get( 1 ); //讀取數據的位置,2 為可讀
if (flag != 2 || index == lastIndex){ //假如不可讀,或未寫入新數據時重復循環
i--;
continue ;
}
lastIndex = index;
System.out.println( "程序 ReadShareMemory:" + System.currentTimeMillis() +
":位置:" + index + " 讀出數據:" + ( char )mbb.get(index));
mbb.put( 0 ,( byte ) 0 ); //置第一個字節為可讀標誌為 0
if (index == 27 ){ //讀完數據後退出
break ;
}
}
}
}
|
在 Eclipse 中運行 WriteShareMemory,然後到命令行下運行 ReadShareMemory,你將會看到 WriteShareMemory 寫一個字符,ReadShareMemory 讀一個。
代碼中使用了讀寫標誌位,和寫入的索引位置,所以在 WriteShareMemory 寫入一個字符後,只有等待 ReadShareMemory 讀出剛寫入的字符後才會寫入第二個字符。實際應用中可以加入更好的通知方式,如文件鎖等。
你也可以查看執行時 c:\swap.mm 文件的內容來驗證這一過程,因為 MappedByteBuffer 在運行時是一種 DirectByteBuffer,所以它能與文件即時的同步內容,無須通過 FileChannel 來 write(buffer) 往文件中手工寫入數據,或 read(buffer) 手工讀數據到內存中。
參考:1. 共享內存在Java中實現和應用
本文鏈接 http://unmi.cc/java-nio-memory-mapping-communicate/, 來自 隔葉黃鶯 Unmi Blog
Java NIO 進程間通信