java多程序與多執行緒
一、程序通訊方法
在說明執行緒通訊前,有必要對程序通訊進行說明;
程序間通訊的方法主要有以下幾種:
(1)管道(Pipe):管道可用於具有親緣關係程序間的通訊,允許一個程序和另一個與它有共同祖先的程序之間進行通訊。
(2)命名管道(named pipe):命名管道克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無親緣關 系 程序間的通訊。命名管道在檔案系統中有對應的檔名。命名管道通過命令mkfifo或系統呼叫mkfifo來建立。
(3)訊號(Signal):訊號是比較複雜的通訊方式,用於通知接受程序有某種事件發生,除了用於程序間通訊外,程序還可以傳送 訊號給程序本身;linux除了支援Unix早期訊號語義函式sigal外,還支援語義符合Posix.1標準的訊號函式sigaction(實際上,該 函式是基於BSD的,BSD為了實現可靠訊號機制,又能夠統一對外介面,用sigaction函式重新實現了signal函式)。
(4) 訊息(Message)佇列:訊息佇列是訊息的連結表,包括Posix訊息佇列system V訊息佇列。有足夠許可權的程序可以向佇列中新增訊息,被賦予讀許可權的程序則可以讀走佇列中的訊息。訊息佇列克服了訊號承載資訊量少,管道只能承載無格式字 節流以及緩衝區大小受限等缺
(5)共享記憶體:使得多個程序可以訪問同一塊記憶體空間,是最快的可用IPC形式。是針對其他通訊機制執行效率較低而設計的。往往與其它通訊機制,如訊號量結合使用,來達到程序間的同步及互斥。
(6)記憶體對映(mapped memory):記憶體對映允許任何多個程序間通訊,每一個使用該機制的程序通過把一個共享的檔案對映到自己的程序地址空間來實現它。
(7)訊號量(semaphore):主要作為程序間以及同一程序不同執行緒之間的同步手段。
(8)套介面(Socket):更為一般的程序間通訊機制,可用於不同機器之間的程序間通訊。起初是由Unix系統的BSD分支開發出來的,但現在一般可以移植到其它類Unix系統上:Linux和System V的變種都支援套接字。
二、執行緒通訊方法
執行緒通訊主要包括兩種方法:
(1)共享記憶體
共享記憶體的方法在前面的("生產者消費者"相關地點都有介紹);下面介紹另外一種形式:
通過內部類實現執行緒的共享變數
/** * 通過內部類實現執行緒的共享變數 * */ public class Innersharethread {public static void main(String[] args) { Mythread mythread = new Mythread(); mythread.getThread().start(); mythread.getThread().start(); mythread.getThread().start(); mythread.getThread().start(); } } class Mythread { int index = 0; private class InnerThread extends Thread { public synchronized void run() { while (true) { System.out.println(Thread.currentThread().getName() + "is running and index is " + index++); } } } public Thread getThread() { return new InnerThread(); } } //在這其中內部類共享類公共類裡面的index變數,並通過對公共類進行加鎖達到方法同步的目的。
(2)管道
主要分為一下步驟:
首先建立管道流,並將管道流的輸入輸出物件進行連結;
將管道流加入到生產物件(執行緒)中;
通過管道流引出輸入輸出流,並在執行緒中對這些流進行操作;
注:管道流的的read的方法是一種阻塞方法;
public class CommunicateWhitPiping { public static void main(String[] args) { /** * 建立管道輸出流 */ PipedOutputStream pos = new PipedOutputStream(); /** * 建立管道輸入流 */ PipedInputStream pis = new PipedInputStream(); try { /** * 將管道輸入流與輸出流連線 此過程也可通過過載的建構函式來實現 */ pos.connect(pis); } catch (IOException e) { e.printStackTrace(); } /** * 建立生產者執行緒 */ Producer p = new Producer(pos); /** * 建立消費者執行緒 */ Consumer c = new Consumer(pis); /** * 啟動執行緒 */ p.start(); c.start(); } } /** * 生產者執行緒(與一個管道輸入流相關聯) * */ class Producer extends Thread { private PipedOutputStream pos; public Producer(PipedOutputStream pos) { this.pos = pos; } public void run() { int i = 8; try { pos.write(i); } catch (IOException e) { e.printStackTrace(); } } } /** * 消費者執行緒(與一個管道輸入流相關聯) * */ class Consumer extends Thread { private PipedInputStream pis; public Consumer(PipedInputStream pis) { this.pis = pis; } public void run() { try { System.out.println(pis.read()); } catch (IOException e) { e.printStackTrace(); } } }
(3)通過呼叫執行緒的公共介面
上圖所示為呼叫公共介面與Actor模型的區別,呼叫公共介面是執行緒A獲取執行緒B的引用,並通過呼叫執行緒B的方法,想B中輸入資訊,從而達到執行緒訊息傳遞的目的,它的缺陷也是顯而易見的,那就是在B中滯留的問題(方法必須在B中返回,A的流程才能繼續執行下去)!
三、其它
眾所周知,在java的多執行緒體系中存在這諸多問題,通訊過程的記憶體共享往往會導致意想不到的錯誤,而管道流的阻塞方法和呼叫介面方法的同步性也會是得有些時候併發程式設計有些困難;現在流線的多執行緒框架為ACTOR體系;在c++中已經相關包的實現(http://c.chinaitlab.com/example/895427.html c++中ACTOR框架的具體介紹),而在java中的衍生語言(其實是基於jvm的語言)scala也提供了Actor機制,具體可進行相關的搜尋