這個指南,你将學習怎樣使用java語言提供的semaphore機制。semaphore是一個控制通路多個共享資源的計數器。
semaphore的内容是由edsger dijkstra引入并在 theos作業系統上第一次使用。
當一個線程想要通路某個共享資源,首先,它必須獲得semaphore。如果semaphore的内部計數器的值大于0,那麼semaphore減少計數器的值并允許通路共享的資源。計數器的值大于0表示,有可以自由使用的資源,是以線程可以通路并使用它們。
另一種情況,如果semaphore的計數器的值等于0,那麼semaphore讓線程進入休眠狀态一直到計數器大于0。計數器的值等于0表示全部的共享資源都正被線程們使用,是以此線程想要通路就必須等到某個資源成為自由的。
當線程使用完共享資源時,他必須放出semaphore為了讓其他線程可以通路共享資源。這個操作會增加semaphore的内部計數器的值。
在這個指南裡,你将學習如何使用semaphore類來實作一種比較特殊的semaphores種類,稱為binary semaphores。這個semaphores種類保護通路共享資源的獨特性,是以semaphore的内部計數器的值隻能是1或者0。為了展示如何使用它,你将要實作一個printqueue類來讓并發任務列印它們的任務。這個printqueue類會受到binary semaphore的保護,是以每次隻能有一個線程可以列印。
準備
指南中的例子是使用eclipse ide 來實作的。如果你使用eclipse 或者其他的ide,例如netbeans, 打開并建立一個新的java任務。
怎麼做呢<b>…</b>
按照這些步驟來實作下面的例子:
它是怎麼工作的…
這個例子的關鍵是printqueue類的printjob()方法。此方法展示了3個你必須遵守的步驟當你使用semaphore來實作critical section時,并保護共享資源的通路:
1. 首先, 你要調用acquire()方法獲得semaphore。
2. 然後, 對共享資源做出必要的操作。
3. 最後, 調用release()方法來釋放semaphore。
另一個重點是printqueue類的構造方法和初始化semaphore對象。你傳遞值1作為此構造方法的參數,那麼你就建立了一個binary semaphore。初始值為1,就保護了通路一個共享資源,在例子中是print queue。
當你開始10個threads,當你開始10個threads時,那麼第一個獲得semaphore的得到critical section的通路權。剩下的線程都會被semaphore阻塞直到那個獲得semaphore的線程釋放它。當這情況發生,semaphore在等待的線程中選擇一個并給予它通路critical section的通路權。全部的任務都會列印文檔,隻是一個接一個的執行。
更多…
semaphore類有另2個版本的 acquire() 方法:
acquireuninterruptibly():acquire()方法是當semaphore的内部計數器的值為0時,阻塞線程直到semaphore被釋放。在阻塞期間,線程可能會被中斷,然後此方法抛出interruptedexception異常。而此版本的acquire方法會忽略線程的中斷而且不會抛出任何異常。
tryacquire():此方法會嘗試擷取semaphore。如果成功,傳回true。如果不成功,傳回false值,并不會被阻塞和等待semaphore的釋放。接下來是你的任務用傳回的值執行正确的行動。
semaphores的公平性
fairness的内容是指全java語言的所有類中,那些可以阻塞多個線程并等待同步資源釋放的類(例如,semaphore)。預設情況下是非公平模式。在這個模式中,當同步資源釋放,就會從等待的線程中任意選擇一個獲得資源,但是這種選擇沒有任何标準。而公平模式可以改變這個行為并強制選擇等待最久時間的線程。
随着其他類的出現,semaphore類的構造函數容許第二個參數。這個參數必需是 boolean 值。如果你給的是 false 值,那麼建立的semaphore就會在非公平模式下運作。如果你不使用這個參數,是跟給false值一樣的結果。如果你給的是true值,那麼你建立的semaphore就會在公平模式下運作。
參見
第八章,測試并發應用:lock接口的監控
<a href="http://ifeve.com/basic-thread-synchronization-7/" target="_blank">第二章,基本線程同步:修改lock的公平性</a>