1. 程式人生 > >執行緒同步工具(二)控制併發訪問多個資源

執行緒同步工具(二)控制併發訪問多個資源

宣告:本文是《 Java 7 Concurrency Cookbook 》的第三章, 作者: Javier Fernández González 譯者:鄭玉婷

控制併發訪問多個資源

在併發訪問資源的控制中,你學習了訊號量(semaphores)的基本知識。

在上個指南,你實現了使用binary semaphores的例子。那種semaphores是用來保護訪問一個共享資源的,或者說一個程式碼片段每次只能被一個執行緒執行。但是semaphores也可以用來保護多個資源的副本,也就是說當你有一個程式碼片段每次可以被多個執行緒執行。

在這個指南中,你將學習怎樣使用semaphore來保護多個資源副本。你將實現的例子會有一個print queue但可以在3個不同的印表機上列印檔案。

準備

指南中的例子是使用 Eclipse IDE 來實現的。如果你使用Eclipse 或者其他的IDE,例如NetBeans, 開啟並建立一個新的java任務。實現在控制併發訪問資源裡描述的例子。

怎麼做呢

按照這些步驟來實現下面的例子:

//1.  如我們之前提到的,你將實現semaphores來修改print queue例子。開啟PrintQueue類並宣告一個boolean array名為 freePrinters。這個array儲存空閒的等待列印任務的和正在列印文件的printers。
private boolean freePrinters[];

//2.   接著,宣告一個名為lockPrinters的Lock物件。將要使用這個物件來保護freePrinters array的訪問。
private Lock lockPrinters;

//3.   修改類的建構函式並初始化新宣告的物件們。freePrinters array 有3個元素,全部初始為真值。semaphore用3作為它的初始值。
public PrintQueue(){

semaphore=new Semaphore(3);
freePrinters=new boolean[3];

for (int i=0; i<3; i++){
	freePrinters[i]=true;
}
lockPrinters=new ReentrantLock();
}

//4.   修改printJob()方法。它接收一個稱為document的物件最為唯一引數。
public void printJob (Object document){

//5.   首先,呼叫acquire()方法獲得semaphore的訪問。由於此方法會丟擲 InterruptedException異常,所以必須加入處理它的程式碼。
try {
	semaphore.acquire();

//6.   接著使用私有方法 getPrinter()來獲得被安排列印任務的印表機的號碼。
int assignedPrinter=getPrinter();

//7.	然後, 隨機等待一段時間來實現模擬列印文件的行。
long duration=(long)(Math.random()*10);
System.out.printf("%s: PrintQueue: Printing a Job in Printer%d during %d seconds\n",Thread.currentThread().getName(), assignedPrinter,duration);
TimeUnit.SECONDS.sleep(duration);

//8.   最後,呼叫release() 方法來解放semaphore並標記印表機為空閒,通過在對應的freePrinters array引索內分配真值。
freePrinters[assignedPrinter]=true;
} catch (InterruptedException e) {
	e.printStackTrace();
} finally {
	semaphore.release();
}

//9.  實現 getPrinter() 方法。它是一個私有方法,返回一個int值,並不接收任何引數。
private int getPrinter() {

//10. 首先,宣告一個int變數來儲存printer的引索值。
int ret=-1;

//11. 然後, 獲得lockPrinters物件 object的訪問。
try {
lockPrinters.lock();

//12. 然後,在freePrinters array內找到第一個真值並在一個變數中儲存這個引索值。修改值為false,因為等會這個印表機就會被使用。
for (int i=0; i<freePrinters.length; i++) {
if (freePrinters[i]){
	ret=i;
	freePrinters[i]=false;
	break;
}
}

//13. 最後,解放lockPrinters物件並返回引索物件為真值。
} catch (Exception e) {
	e.printStackTrace();
} finally {
	lockPrinters.unlock();
}
return ret;

//14. Job 和 Core 類不做任何改變。

它是怎麼工作的…

在例子中的PrintQueue類的關鍵是:Semaphore物件建立的構造方法是使用3作為引數的。這個例子中,前3個呼叫acquire() 方法的執行緒會獲得臨界區的訪問權,其餘的都會被阻塞 。當一個執行緒結束臨界區的訪問並解放semaphore時,另外的執行緒才可能獲得訪問權。

在這個臨界區,執行緒獲得被分配列印的印表機的引索值。例子的這部分讓例子更真實,而且它沒有使用任何與semaphores相關的程式碼。以下的裁圖展示了這個例子的執行輸出:

每個文件都被安排到第一個空閒的印表機列印。

更多…

The acquire(), acquireUninterruptibly(), tryAcquire(),和release()方法有一個外加的包含一個int引數的版本。這個引數表示 執行緒想要獲取或者釋放semaphore的許可數。也可以這樣說,這個執行緒想要刪除或者新增到semaphore的內部計數器的單位數量。在這個例子中acquire(), acquireUninterruptibly(), 和tryAcquire() 方法, 如果計數器的值小於許可值,那麼執行緒就會被阻塞直到計數器到達或者大於許可值。

參見