執行緒同步工具(二)控制併發訪問多個資源
宣告:本文是《 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() 方法, 如果計數器的值小於許可值,那麼執行緒就會被阻塞直到計數器到達或者大於許可值。
參見