簡要分析Java多進程編程的并發(fā)控制
進程間的通訊無非就是讀寫文件,socket通訊或者使用共享內(nèi)存。
java沒法管理內(nèi)存,其實他也是靠創(chuàng)建映像文件來實現(xiàn)的。
共享內(nèi)存在java中的實現(xiàn)
在jdk1.4中提供的類MappedByteBuffer為我們實現(xiàn)共享內(nèi)存提供了較好的方法。該緩沖區(qū)實際上是一個磁盤文件的內(nèi)存映像。二者的變化將保持同步,即內(nèi)存數(shù)據(jù)發(fā)生變化會立刻反映到磁盤文件中,這樣會有效的保證共享內(nèi)存的實現(xiàn)。
將共享內(nèi)存和磁盤文件建立聯(lián)系的是文件通道類:FileChannel。該類的加入是JDK為了統(tǒng)一對外部設備(文件、網(wǎng)絡接口等)的訪問方法,并且加強了多線程對同一文件進行存取的安全性。例如讀寫操作統(tǒng)一成read和write。這里只是用它來建立共享內(nèi)存用,它建立了共享內(nèi)存和磁盤文件之間的一個通道。
打開一個文件建立一個文件通道可以用RandomAccessFile類中的方法getChannel。該方法將直接返回一個文件通道。該文件通道由于對應的文件設為隨機存取文件,一方面可以進行讀寫兩種操作,另一方面使用它不會破壞映像文件的內(nèi)容(如果用FileOutputStream直接打開一個映像文件會將該文件的大小置為0,當然數(shù)據(jù)會全部丟失)。這里,如果用 FileOutputStream和FileInputStream則不能理想的實現(xiàn)共享內(nèi)存的要求,因為這兩個類同時實現(xiàn)自由的讀寫操作要困難得多。
下面的代碼實現(xiàn)了如上功能,它的作用類似UNIX系統(tǒng)中的mmap函數(shù)。
// 獲得一個只讀的隨機存取文件對象 RandomAccessFile RAFile = new RandomAccessFile(filename,"r"); // 獲得相應的文件通道 FileChannel fc = RAFile.getChannel(); // 取得文件的實際大小,以便映像到共享內(nèi)存 int size = (int)fc.size(); // 獲得共享內(nèi)存緩沖區(qū),該共享內(nèi)存只讀 MappedByteBuffer mapBuf = fc.map(FileChannel.MAP_RO,0,size); // 獲得一個可讀寫的隨機存取文件對象 RAFile = new RandomAccessFile(filename,"rw"); // 獲得相應的文件通道 fc = RAFile.getChannel(); // 取得文件的實際大小,以便映像到共享內(nèi)存 size = (int)fc.size(); // 獲得共享內(nèi)存緩沖區(qū),該共享內(nèi)存可讀寫 mapBuf = fc.map(FileChannel.MAP_RW,0,size); // 獲取頭部消息:存取權限 mode = mapBuf.getInt();
如果多個應用映像同一文件名的共享內(nèi)存,則意味著這多個應用共享了同一內(nèi)存數(shù)據(jù)。這些應用對于文件可以具有同等存取權限,一個應用對數(shù)據(jù)的刷新會更新到多個應用中。
為了防止多個應用同時對共享內(nèi)存進行寫操作,可以在該共享內(nèi)存的頭部信息加入寫操作標志。該共享內(nèi)存的頭部基本信息至少有:
int Length; // 共享內(nèi)存的長度。 int mode; // 該共享內(nèi)存目前的存取模式。
共享內(nèi)存的頭部信息是類的私有信息,在多個應用可以對同一共享內(nèi)存執(zhí)行寫操作時,開始執(zhí)行寫操作和結束寫操作時,需調(diào)用如下方法:
public boolean StartWrite() { if(mode == 0) { // 標志為0,則表示可寫 mode = 1; // 置標志為1,意味著別的應用不可寫該共享內(nèi)存 mapBuf.flip(); mapBuf.putInt(mode); // 寫如共享內(nèi)存的頭部信息 return true; } else { return false; // 指明已經(jīng)有應用在寫該共享內(nèi)存,本應用不可寫該共享內(nèi)存 } } public boolean StopWrite() { mode = 0; // 釋放寫權限 mapBuf.flip(); mapBuf.putInt(mode); // 寫入共享內(nèi)存頭部信息 return true; }
這里提供的類文件mmap.java封裝了共享內(nèi)存的基本接口,讀者可以用該類擴展成自己需要的功能全面的類。
如果執(zhí)行寫操作的應用異常中止,那么映像文件的共享內(nèi)存將不再能執(zhí)行寫操作。為了在應用異常中止后,寫操作禁止標志自動消除,必須讓運行的應用獲知退出的應用。在多線程應用中,可以用同步方法獲得這樣的效果,但是在多進程中,同步是不起作用的。方法可以采用的多種技巧,這里只是描述一可能的實現(xiàn):采用文件鎖的方式。寫共享內(nèi)存應用在獲得對一個共享內(nèi)存寫權限的時候,除了判斷頭部信息的寫權限標志外,還要判斷一個臨時的鎖文件是否可以得到,如果可以得到,則即使頭部信息的寫權限標志為1(上述),也可以啟動寫權限,其實這已經(jīng)表明寫權限獲得的應用已經(jīng)異常退出,這段代碼如下:
// 打開一個臨時的文件,注意同一共享內(nèi)存,該文件名要相同,可以在共享文件名后加后綴“.lock”。 RandomAccessFile fis = new RandomAccessFile("shm.lock","rw"); // 獲得文件通道 FileChannel lockfc = fis.getChannel(); // 獲得文件的獨占鎖,該方法不產(chǎn)生堵塞,立刻返回 FileLock flock = lockfc.tryLock(); // 如果為空,則表明已經(jīng)有應用占有該鎖 if(flock == null) { ...// 不能執(zhí)行寫操作 }else { ...// 可以執(zhí)行寫操作 }
該鎖會在應用異常退出后自動釋放,這正是該處所需要的方法。
相關文章
以Spring Boot的方式顯示圖片或下載文件到瀏覽器的示例代碼
這篇文章主要介紹了以Spring Boot的方式顯示圖片或下載文件到瀏覽器的示例代碼,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01使用Spring MVC攔截器實現(xiàn)日志記錄的方法
本篇文章主要介紹了使用Spring MVC攔截器實現(xiàn)日志記錄的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-04-04解決rocketmq-client查詢手動發(fā)送消息異常問題
這篇文章主要介紹了解決rocketmq-client查詢手動發(fā)送消息異常問題,具有很好的參考價值,希望對大家大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08詳解maven的setting配置文件中mirror和repository的區(qū)別
這篇文章主要介紹了詳解maven的setting配置文件中mirror和repository的區(qū)別,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-12-12springboot如何為web層添加統(tǒng)一請求前綴
這篇文章主要介紹了springboot如何為web層添加統(tǒng)一請求前綴,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02淺談Java中Int、Integer、Integer.valueOf()、new Integer()之間的區(qū)別
本文主要介紹了淺談Java中Int、Integer、Integer.valueOf()、new Integer()之間的區(qū)別,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11