共享內存對應應用開發的意義
對熟知UNIX系統應用開發的程序員來說
IPC(InterProcess Communication)機制是非常熟悉的
IPC基本包括共享內存
信號燈操作
消息隊列
信號處理等部分
是開發應用中非常重要的必不可少的工具
其中共享內存IPC機制的關鍵
對於數據共享
系統快速查詢
動態配置
減少資源耗費等均有獨到的優點
對應UNIX系統來說
共享內存分為一般共享內存和映像文件共享內存兩種
而對應 Windows
實際上只有映像文件共享內存一種
所以java應用中也是只能創建映像文件共享內存
在java語言中
基本上沒有提及共享內存這個概念
但是
在某一些應用中
共享內存確實非常有用
例如采用java語言的分布式應用系統中
存在著大量的分布式共享對象
很多時候需要查詢這些對象的狀態
以查看系統是否運行正常或者了解這些對象的目前的一些統計數據和狀態
如果采用網絡通信的方式
顯然會增加應用的額外負擔
也增加了一些不必要的應用編程
而如果采用共享內存的方式
則可以直接通過共享內存查看對象的狀態數據和統計數據
從而減少了一些不必要的麻煩
共享內存的使用有如下幾個特點
可以被多個進程打開訪問
讀寫操作的進程在執行讀寫操作時其他進程不能進行寫操作
多個進程可以交替對某一共享內存執行寫操作
一個進程執行了內存的寫操作後
不影響其他進程對該內存的訪問
同時其他進程對更新後的內存具有可見性
在進程執行寫操作時如果異常退出
對其他進程寫操作禁止應自動解除
相對共享文件
數據訪問的方便性和效率有
另外
共享內存的使用上有如下情況
獨占的寫操作
相應有獨占的寫操作等待隊列
獨占的寫操作本身不會發生數據的一致性問題
共享的寫操作
相應有共享的寫操作等待隊列
共享的寫操作則要注意防止發生數據的一致性問題
獨占的讀操作
相應有共享的讀操作等待隊列
共享的讀操作
相應有共享的讀操作等待隊列
一般情況下
我們只是關心第一二種情況
共享內存在java中的實現
在jdk
中提供的類MappedByteBuffer為我們實現共享內存提供了較好的方法
該緩沖區實際上是一個磁盤文件的內存映像
二者的變化將保持同步
即內存數據發生變化會立刻反映到磁盤文件中
這樣會有效的保證共享內存的實現
將共享內存和磁盤文件建立聯系的是文件通道類
FileChannel
該類的加入是JDK為了統一對外部設備(文件
網絡接口等)的訪問方法
並且加強了多線程對同一文件進行存取的安全性
例如讀寫操作統一成read和write
這裡只是用它來建立共享內存用
它建立了共享內存和磁盤文件之間的一個通道
打開一個文件建立一個文件通道可以用RandomAccessFile類中的方法getChannel
該方法將直接返回一個文件通道
該文件通道由於對應的文件設為隨機存取文件
一方面可以進行讀寫兩種操作
另一方面使用它不會破壞映像文件的內容(如果用FileOutputStream直接打開一個映像文件會將該文件的大小置為
當然數據會全部丟失)
這裡
如果用 FileOutputStream和FileInputStream則不能理想的實現共享內存的要求
因為這兩個類同時實現自由的讀寫操作要困難得多
下面的代碼實現了如上功能
它的作用類似UNIX系統中的mmap函數
// 獲得一個只讀的隨機存取文件對象
RandomAccessFile RAFile = new RandomAccessFile(filename
r
);
// 獲得相應的文件通道
FileChannel fc = RAFile
getChannel();
// 取得文件的實際大小
以便映像到共享內存
int size = (int)fc
size();
// 獲得共享內存緩沖區
該共享內存只讀
MappedByteBuffer mapBuf = fc
map(FileChannel
MAP_RO
size);
// 獲得一個可讀寫的隨機存取文件對象
RAFile = new RandomAccessFile(filename
rw
);
// 獲得相應的文件通道
fc = RAFile
getChannel();
// 取得文件的實際大小
以便映像到共享內存
size = (int)fc
size();
// 獲得共享內存緩沖區
該共享內存可讀寫
mapBuf = fc
map(FileChannel
MAP_RW
size);
// 獲取頭部消息
存取權限
mode = mapBuf
getInt();
如果多個應用映像同一文件名的共享內存
則意味著這多個應用共享了同一內存數據
這些應用對於文件可以具有同等存取權限
一個應用對數據的刷新會更新到多個應用中
為了防止多個應用同時對共享內存進行寫操作
可以在該共享內存的頭部信息加入寫操作標志
該共享內存的頭部基本信息至少有
int Length
// 共享內存的長度
int mode; // 該共享內存目前的存取模式
共享內存的頭部信息是類的私有信息
在多個應用可以對同一共享內存執行寫操作時
開始執行寫操作和結束寫操作時
需調用如下方法
public boolean StartWrite()
{
if(mode ==
) { // 標志為
則表示可寫
mode =
; // 置標志為
意味著別的應用不可寫該共享內存
mapBuf
flip();
mapBuf
putInt(mode); // 寫如共享內存的頭部信息
return true;
}
else {
return false; // 指明已經有應用在寫該共享內存
本應用不可寫該共享內存
}
}
public boolean StopWrite()
{
mode =
; // 釋放寫權限
mapBuf
flip();
mapBuf
putInt(mode); // 寫入共享內存頭部信息
return true;
}
這裡提供的類文件mmap
java封裝了共享內存的基本接口
讀者可以用該類擴展成自己需要的功能全面的類
如果執行寫操作的應用異常中止
那麼映像文件的共享內存將不再能執行寫操作
為了在應用異常中止後
寫操作禁止標志自動消除
必須讓運行的應用獲知退出的應用
在多線程應用中
可以用同步方法獲得這樣的效果
但是在多進程中
同步是不起作用的
方法可以采用的多種技巧
這裡只是描述一可能的實現
采用文件鎖的方式
寫共享內存應用在獲得對一個共享內存寫權限的時候
除了判斷頭部信息的寫權限標志外
還要判斷一個臨時的鎖文件是否可以得到
如果可以得到
則即使頭部信息的寫權限標志為
(上述)
也可以啟動寫權限
其實這已經表明寫權限獲得的應用已經異常退出
這段代碼如下
// 打開一個臨時的文件
注意同一共享內存
該文件名要相同
可以在共享文件名後加後綴
lock
RandomAccessFile fis = new RandomAccessFile(
shm
lock
rw
);
// 獲得文件通道
FileChannel lockfc = fis
getChannel();
// 獲得文件的獨占鎖
該方法不產生堵塞
立刻返回
FileLock flock = lockfc
tryLock();
// 如果為空
則表明已經有應用占有該鎖
if(flock == null) {
// 不能執行寫操作
}
else {
// 可以執行寫操作
}
該鎖會在應用異常退出後自動釋放
這正是該處所需要的方法
共享內存在java中的應用
共享內存在java應用中
經常有如下兩種種應用
永久對象配置
在java服務器應用中
用戶可能會在運行過程中配置一些參數
而這些參數需要永久有效
當服務器應用重新啟動後
這些配置參數仍然可以對應用起作用
這就可以用到該文中的共享內存
該共享內存中保存了服務器的運行參數和一些對象運行特性
可以在應用啟動時讀入以啟用以前配置的參數
查詢共享數據
一個應用(例 sys
java)是系統的服務進程
其系統的運行狀態記錄在共享內存中
其中運行狀態可能是不斷變化的
為了隨時了解系統的運行狀態
啟動另一個應用(例 mon
java)
該應用查詢該共享內存
匯報系統的運行狀態
可見
共享內存在java應用中還是很有用的
只要組織好共享內存的數據結構
共享內存就可以在應用開發中發揮很不錯的作用
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19194.html