1. 程式人生 > >基本執行緒同步(七)修改Lock的公平性

基本執行緒同步(七)修改Lock的公平性

宣告:本文是《 Java 7 Concurrency Cookbook 》的第二章,作者: Javier Fernández González  譯者:許巧輝 校對:方騰飛

修改Lock的公平性

在ReentrantLock類和 ReentrantReadWriteLock類的構造器中,允許一個名為fair的boolean型別引數,它允許你來控制這些類的行為。預設值為 false,這將啟用非公平模式。在這個模式中,當有多個執行緒正在等待一把鎖(ReentrantLock或者 ReentrantReadWriteLock),這個鎖必須選擇它們中間的一個來獲得進入臨界區,選擇任意一個是沒有任何標準的。true值將開啟公平 模式。在這個模式中,當有多個執行緒正在等待一把鎖(ReentrantLock或者ReentrantReadWriteLock),這個鎖必須選擇它們 中間的一個來獲得進入臨界區,它將選擇等待時間最長的執行緒。考慮到之前解釋的行為只是使用lock()和unlock()方法。由於tryLock()方 法並不會使執行緒進入睡眠,即使Lock介面正在被使用,這個公平屬性並不會影響它的功能。

在這個指南中,我們將修改使用Lock同步程式碼塊食譜示例來使用這個屬性,並且觀察公平與非公平模式之間的差別。

準備工作…

我們將要修改使用Lock同步程式碼塊食譜的示例,所以閱讀那個食譜來實現這個示例。

如何做…

按以下步驟來實現的這個例子:

1.實現有使用Lock同步程式碼塊食譜中解釋的示例。

2.在PrintQueue類,修改Lock物件的構造,如下:

private Lock queueLock=new ReentrantLock(true);

3.修改printJob()方法,使用兩個程式碼塊分離列印的模擬,在它們之間釋放鎖。

public void printJob(Object document){
queueLock.lock();
try {
Long duration=(long)(Math.random()*10000);
System.out.println(Thread.currentThread().getName()+":
PrintQueue: Printing a Job during "+(duration/1000)+" seconds");
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
queueLock.unlock();
}
queueLock.lock();
try {
Long duration=(long)(Math.random()*10000);
System.out.println(Thread.currentThread().getName()+":
PrintQueue: Printing a Job during "+(duration/1000)+" seconds");
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
queueLock.unlock();
}
}

4.修改Main類中,啟動執行緒的程式碼塊。新的程式碼塊如下:

for (int i=0; i<10; i++){
thread[i].start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

它是如何工作的…

在以下截圖中,你可以看到執行這個例子的一個部分輸出:

6

所有執行緒都建立一個0.1秒的差異,第一需要獲取鎖的控制權的執行緒是Thread0,然後是Thread1,以此類推。當Thread0正在執行第一個由鎖 保護的程式碼塊時,有9個執行緒正在那個程式碼塊上等待執行。當Thread0釋放鎖,它需要馬上再次獲取鎖,所以我們有10個執行緒試圖獲取這個鎖。當啟用程式碼 模式,Lock介面將會選擇Thread1,它是在這個鎖上等待最長時間的執行緒。然後,選擇Thread2,然後是Thread3,以此類推。直到所有線 程都通過了這個鎖保護的第一個程式碼塊,否則,沒有一個執行緒能執行該鎖保護的第二個程式碼塊。

一旦所有執行緒已經執行完由這個鎖保護的第一個程式碼塊,再次輪到Thread0。然後,輪到Thread1,以此類推。

為了看與非公平模式的差異,改變傳入鎖構造器的引數,傳入false值。在以下截圖中,你可以看到修改示例後的執行結果:

7

在這種情況下,執行緒按被建立的順序執行,但每個執行緒各自執行兩個受保護的程式碼塊。然而,這種行為的原因是沒有保證的,正如之前解釋的,這個鎖將選擇任意一個執行緒獲得訪問保護程式碼塊。在這種情況下,JVM不能保證執行緒的執行順序。

不止這些…

讀/寫鎖在它們的構造器中也有公平引數。這個引數在這種鎖中的行為與本指南的解釋是一樣的。

參見

  • 在第2章,基本執行緒同步中使用Lock同步程式碼塊的指南
  • 在第2章,基本執行緒同步中使用讀/寫鎖同步資料訪問的指南
  • 在第7章,制訂併發類中實現一個自定義的Lock類的指南