簡(jiǎn)要分析Java多進(jìn)程編程的并發(fā)控制
進(jìn)程間的通訊無(wú)非就是讀寫(xiě)文件,socket通訊或者使用共享內(nèi)存。
java沒(méi)法管理內(nèi)存,其實(shí)他也是靠創(chuàng)建映像文件來(lái)實(shí)現(xiàn)的。
共享內(nèi)存在java中的實(shí)現(xiàn)
在jdk1.4中提供的類(lèi)MappedByteBuffer為我們實(shí)現(xiàn)共享內(nèi)存提供了較好的方法。該緩沖區(qū)實(shí)際上是一個(gè)磁盤(pán)文件的內(nèi)存映像。二者的變化將保持同步,即內(nèi)存數(shù)據(jù)發(fā)生變化會(huì)立刻反映到磁盤(pán)文件中,這樣會(huì)有效的保證共享內(nèi)存的實(shí)現(xiàn)。
將共享內(nèi)存和磁盤(pán)文件建立聯(lián)系的是文件通道類(lèi):FileChannel。該類(lèi)的加入是JDK為了統(tǒng)一對(duì)外部設(shè)備(文件、網(wǎng)絡(luò)接口等)的訪(fǎng)問(wèn)方法,并且加強(qiáng)了多線(xiàn)程對(duì)同一文件進(jìn)行存取的安全性。例如讀寫(xiě)操作統(tǒng)一成read和write。這里只是用它來(lái)建立共享內(nèi)存用,它建立了共享內(nèi)存和磁盤(pán)文件之間的一個(gè)通道。
打開(kāi)一個(gè)文件建立一個(gè)文件通道可以用RandomAccessFile類(lèi)中的方法getChannel。該方法將直接返回一個(gè)文件通道。該文件通道由于對(duì)應(yīng)的文件設(shè)為隨機(jī)存取文件,一方面可以進(jìn)行讀寫(xiě)兩種操作,另一方面使用它不會(huì)破壞映像文件的內(nèi)容(如果用FileOutputStream直接打開(kāi)一個(gè)映像文件會(huì)將該文件的大小置為0,當(dāng)然數(shù)據(jù)會(huì)全部丟失)。這里,如果用 FileOutputStream和FileInputStream則不能理想的實(shí)現(xiàn)共享內(nèi)存的要求,因?yàn)檫@兩個(gè)類(lèi)同時(shí)實(shí)現(xiàn)自由的讀寫(xiě)操作要困難得多。
下面的代碼實(shí)現(xiàn)了如上功能,它的作用類(lèi)似UNIX系統(tǒng)中的mmap函數(shù)。
// 獲得一個(gè)只讀的隨機(jī)存取文件對(duì)象 RandomAccessFile RAFile = new RandomAccessFile(filename,"r"); // 獲得相應(yīng)的文件通道 FileChannel fc = RAFile.getChannel(); // 取得文件的實(shí)際大小,以便映像到共享內(nèi)存 int size = (int)fc.size(); // 獲得共享內(nèi)存緩沖區(qū),該共享內(nèi)存只讀 MappedByteBuffer mapBuf = fc.map(FileChannel.MAP_RO,0,size); // 獲得一個(gè)可讀寫(xiě)的隨機(jī)存取文件對(duì)象 RAFile = new RandomAccessFile(filename,"rw"); // 獲得相應(yīng)的文件通道 fc = RAFile.getChannel(); // 取得文件的實(shí)際大小,以便映像到共享內(nèi)存 size = (int)fc.size(); // 獲得共享內(nèi)存緩沖區(qū),該共享內(nèi)存可讀寫(xiě) mapBuf = fc.map(FileChannel.MAP_RW,0,size); // 獲取頭部消息:存取權(quán)限 mode = mapBuf.getInt();
如果多個(gè)應(yīng)用映像同一文件名的共享內(nèi)存,則意味著這多個(gè)應(yīng)用共享了同一內(nèi)存數(shù)據(jù)。這些應(yīng)用對(duì)于文件可以具有同等存取權(quán)限,一個(gè)應(yīng)用對(duì)數(shù)據(jù)的刷新會(huì)更新到多個(gè)應(yīng)用中。
為了防止多個(gè)應(yīng)用同時(shí)對(duì)共享內(nèi)存進(jìn)行寫(xiě)操作,可以在該共享內(nèi)存的頭部信息加入寫(xiě)操作標(biāo)志。該共享內(nèi)存的頭部基本信息至少有:
int Length; // 共享內(nèi)存的長(zhǎng)度。 int mode; // 該共享內(nèi)存目前的存取模式。
共享內(nèi)存的頭部信息是類(lèi)的私有信息,在多個(gè)應(yīng)用可以對(duì)同一共享內(nèi)存執(zhí)行寫(xiě)操作時(shí),開(kāi)始執(zhí)行寫(xiě)操作和結(jié)束寫(xiě)操作時(shí),需調(diào)用如下方法:
public boolean StartWrite()
{
if(mode == 0) { // 標(biāo)志為0,則表示可寫(xiě)
mode = 1; // 置標(biāo)志為1,意味著別的應(yīng)用不可寫(xiě)該共享內(nèi)存
mapBuf.flip();
mapBuf.putInt(mode); // 寫(xiě)如共享內(nèi)存的頭部信息
return true;
}
else {
return false; // 指明已經(jīng)有應(yīng)用在寫(xiě)該共享內(nèi)存,本應(yīng)用不可寫(xiě)該共享內(nèi)存
}
}
public boolean StopWrite()
{
mode = 0; // 釋放寫(xiě)權(quán)限
mapBuf.flip();
mapBuf.putInt(mode); // 寫(xiě)入共享內(nèi)存頭部信息
return true;
}
這里提供的類(lèi)文件mmap.java封裝了共享內(nèi)存的基本接口,讀者可以用該類(lèi)擴(kuò)展成自己需要的功能全面的類(lèi)。
如果執(zhí)行寫(xiě)操作的應(yīng)用異常中止,那么映像文件的共享內(nèi)存將不再能執(zhí)行寫(xiě)操作。為了在應(yīng)用異常中止后,寫(xiě)操作禁止標(biāo)志自動(dòng)消除,必須讓運(yùn)行的應(yīng)用獲知退出的應(yīng)用。在多線(xiàn)程應(yīng)用中,可以用同步方法獲得這樣的效果,但是在多進(jìn)程中,同步是不起作用的。方法可以采用的多種技巧,這里只是描述一可能的實(shí)現(xiàn):采用文件鎖的方式。寫(xiě)共享內(nèi)存應(yīng)用在獲得對(duì)一個(gè)共享內(nèi)存寫(xiě)權(quán)限的時(shí)候,除了判斷頭部信息的寫(xiě)權(quán)限標(biāo)志外,還要判斷一個(gè)臨時(shí)的鎖文件是否可以得到,如果可以得到,則即使頭部信息的寫(xiě)權(quán)限標(biāo)志為1(上述),也可以啟動(dòng)寫(xiě)權(quán)限,其實(shí)這已經(jīng)表明寫(xiě)權(quán)限獲得的應(yīng)用已經(jīng)異常退出,這段代碼如下:
// 打開(kāi)一個(gè)臨時(shí)的文件,注意同一共享內(nèi)存,該文件名要相同,可以在共享文件名后加后綴“.lock”。
RandomAccessFile fis = new RandomAccessFile("shm.lock","rw");
// 獲得文件通道
FileChannel lockfc = fis.getChannel();
// 獲得文件的獨(dú)占鎖,該方法不產(chǎn)生堵塞,立刻返回
FileLock flock = lockfc.tryLock();
// 如果為空,則表明已經(jīng)有應(yīng)用占有該鎖
if(flock == null) {
...// 不能執(zhí)行寫(xiě)操作
}else {
...// 可以執(zhí)行寫(xiě)操作
}
該鎖會(huì)在應(yīng)用異常退出后自動(dòng)釋放,這正是該處所需要的方法。
相關(guān)文章
Spring boot調(diào)用Oracle存儲(chǔ)過(guò)程的兩種方式及完整代碼
這篇文章主要給大家介紹了關(guān)于Spring boot調(diào)用Oracle存儲(chǔ)過(guò)程的兩種方式及完整代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
java中abstract修改類(lèi)的實(shí)例方法
在本篇文章里小編給各位分享了一篇關(guān)于java中abstract修改類(lèi)的實(shí)例方法,有需要的朋友們可以學(xué)習(xí)下。2020-12-12

