天天看點

Java程序之間通信方式&線程之間通信的方式

程序之間通信方式

(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 的“共享記憶體”的實作原理:

Java程式之間通信方式&線程之間通信的方式

知道了實作原理之後,下面用代碼來示範兩個程序間用記憶體映射檔案來進行資料通信。代碼 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;

public

class

WriteShareMemory {

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, 

1024

);

//清除檔案内容

for

(

int

i=

;i<

1024

;i++){

mbb.put(i,(

byte

)

);

}

//從檔案的第二個位元組開始,依次寫入 A-Z 字母,第一個位元組指明了目前操作的位置

for

(

int

i=

65

;i<

91

;i++){

int

index = i-

63

;

int

flag = mbb.get(

); 

//可讀标置第一個位元組為 0

if

(flag != 

){ 

//不是可寫标示 0,則重複循環,等待

i --;

continue

;

}

mbb.put(

,(

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(

,(

byte

)

2

); 

//置可讀資料标志第一個位元組為 2

Thread.sleep(

513

);

}

}

}

本文原始連結 http://unmi.cc/java-nio-memory-mapping-communicate/, 來自 隔葉黃莺 Unmi Blog

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;

public

class

ReadShareMemory {

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, 

1024

);

int

lastIndex = 

;

for

(

int

i=

1

;i<

27

;i++){

int

flag = mbb.get(

); 

//取讀寫資料的标志

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(

,(

byte

)

); 

//置第一個位元組為可讀标志為 0

if

(index == 

27

){ 

//讀完資料後退出

break

;

}

}

}

}

在 Eclipse 中運作 WriteShareMemory,然後到指令行下運作 ReadShareMemory,你将會看到 WriteShareMemory 寫一個字元,ReadShareMemory 讀一個。

Java程式之間通信方式&amp;線程之間通信的方式

代碼中使用了讀寫标志位,和寫入的索引位置,是以在 WriteShareMemory 寫入一個字元後,隻有等待 ReadShareMemory 讀出剛寫入的字元後才會寫入第二個字元。實際應用中可以加入更好的通知方式,如檔案鎖等。

你也可以檢視執行時 c:\swap.mm 檔案的内容來驗證這一過程,因為 MappedByteBuffer 在運作時是一種 DirectByteBuffer,是以它能與檔案即時的同步内容,無須通過 FileChannel 來 write(buffer) 往檔案中手工寫入資料,或 read(buffer) 手工讀資料到記憶體中。

線程之間通信方式

TODO

繼續閱讀