NIO详解(七):进程间通信(MappedByteBuffer)
1. 前言
最近在研究Java進程間通信,為了了解Java中的SharedMemory共享內存。我特地去研究了一些Java NIO進程間通信的方式。
2. Java NIO MappedByteBuffer原理
傳統的進程間通信的方式有大致如下幾種:
- (1) 管道(PIPE)
- (2) 命名管道(FIFO)
- (3) 信號量(Semphore)
- (4) 消息隊列(MessageQueue)
- (5) 共享內存(SharedMemory)
- (6) Socket
Java如何支持進程間通信。我們把Java進程理解為JVM進程。很明顯,傳統的這些大部分技術是無法被我們的應用程序利用了(這些進程間通信都是靠系統調用來實現的)。但是Java也有很多方法可以進行進程間通信的。 Java nio的MappedByteBuffer也可以通過內存映射文件來實現進程間通信(共享內存)。
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。
3. 實現代碼
/*** locate com.basic* Created by MasterTj on 2019/3/7.* "共享內存" 讀出數據*/ public class ReadShareMemory {/*** @param args* @throws Exception*/public static void main(String[] args) throws Exception {RandomAccessFile raf = new RandomAccessFile("d:/swap.mm", "rw");FileChannel fc = raf.getChannel();MappedByteBuffer mbb = fc.map(FileChannel.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); //置第一個字節為可讀標志為 0if(index == 27){ //讀完數據后退出break;}}} } /*** locate com.basic* Created by MasterTj on 2019/3/7.* 從共享內存中讀取數據*/ public class WriteShareMemory {/*** @param args* @throws Exception*/public static void main(String[] args) throws Exception {RandomAccessFile raf = new RandomAccessFile("d:/swap.mm", "rw");FileChannel fc = raf.getChannel();MappedByteBuffer mbb = fc.map(FileChannel.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); //可讀標置第一個字節為 0if(flag != 0){ //不是可寫標示 0,則重復循環,等待i --;continue;}mbb.put(0,(byte)1); //正在寫數據,標志第一個字節為 1mbb.put(1,(byte)(index)); //寫數據的位置System.out.println("程序 WriteShareMemory:"+System.currentTimeMillis() +":位置:" + index +" 寫入數據:" + (char)i);mbb.put(index,(byte)i);//index 位置寫入數據mbb.put(0,(byte)2); //置可讀數據標志第一個字節為 2Thread.sleep(513);}} }代碼中使用了讀寫標志位,和寫入的索引位置,所以在 WriteShareMemory 寫入一個字符后,只有等待 ReadShareMemory 讀出剛寫入的字符后才會寫入第二個字符。實際應用中可以加入更好的通知方式,如文件鎖等。
你也可以查看執行時 c:\swap.mm 文件的內容來驗證這一過程,因為 MappedByteBuffer 在運行時是一種 DirectByteBuffer,所以它能與文件即時的同步內容,無須通過 FileChannel 來 write(buffer) 往文件中手工寫入數據,或 read(buffer) 手工讀數據到內存中。
總結
以上是生活随笔為你收集整理的NIO详解(七):进程间通信(MappedByteBuffer)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据结构和算法分析:第二章 算法分析
- 下一篇: Java基础:JavaNIO 之 内存映