ReentrantLock(重入鎖)以及公平性

轉自 併發編程網http://ifeve.com/reentrantlock-and-fairness/


簡介

ReentrantLock的實現不僅可以替代隱式的synchronized關鍵字,而且能夠提供超過關鍵字本身的多種功能。
這裏提到一個鎖獲取的公平性問題,如果在絕對時間上,先對鎖進行獲取的請求一定被先滿足,那麼這個鎖是公平的,反之,是不公平的,也就是說等待時間最長的線程最有機會獲取鎖,也可以說鎖的獲取是有序的。ReentrantLock這個鎖提供了一個構造函數,能夠控制這個鎖是否是公平的。
而鎖的名字也是說明了這個鎖具備了重複進入的可能,也就是說能夠讓當前線程多次的進行對鎖的獲取操作,這樣的最大次數限制是Integer.MAX_VALUE,約21億次左右。
事實上公平的鎖機制往往沒有非公平的效率高,因爲公平的獲取鎖沒有考慮到操作系統對線程的調度因素,這樣造成JVM對於等待中的線程調度次序和操作系統對線程的調度之間的不匹配。對於鎖的快速且重複的獲取過程中,連續獲取的概率是非常高的,而公平鎖會壓制這種情況,雖然公平性得以保障,但是響應比卻下降了,但是並不是任何場景都是以TPS作爲唯一指標的,因爲公平鎖能夠減少“飢餓”發生的概率,等待越久的請求越是能夠得到優先滿足。

實現分析

在ReentrantLock中,對於公平和非公平的定義是通過對同步器AbstractQueuedSynchronizer的擴展加以實現的,也就是在tryAcquire的實現上做了語義的控制。

非公平的獲取語義:

01 final boolean nonfairTryAcquire(int acquires) {
02     final Thread current = Thread.currentThread();
03     int c = getState();
04     if (c == 0) {
05         if (compareAndSetState(0, acquires)) {
06             setExclusiveOwnerThread(current);
07             return true;
08         }
09     else if (current == getExclusiveOwnerThread()) {
10         int nextc = c + acquires;
11                 if (nextc < 0// overflow
12             throw new Error("Maximum lock count exceeded");
13         setState(nextc);
14         return true;
15     }
16     return false;
17 }

上述邏輯主要包括:

  • 如果當前狀態爲初始狀態,那麼嘗試設置狀態;
  • 如果狀態設置成功後就返回;
  • 如果狀態被設置,且獲取鎖的線程又是當前線程的時候,進行狀態的自增;
  • 如果未設置成功狀態且當前線程不是獲取鎖的線程,那麼返回失敗。

公平的獲取語義:

01 protected final boolean tryAcquire(int acquires) {
02     final Thread current = Thread.currentThread();
03     int c = getState();
04     if (c == 0) {
05         if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
06             setExclusiveOwnerThread(current);
07             return true;
08         }
09     else if (current == getExclusiveOwnerThread()) {
10         int nextc = c + acquires;
11         if (nextc < 0)
12             throw new Error("Maximum lock count exceeded");
13         setState(nextc);
14         return true;
15     }
16     return false;
17 }

上述邏輯相比較非公平的獲取,僅加入了當前線程(Node)之前是否有前置節點在等待的判斷。hasQueuedPredecessors()方法命名有些歧義,其實應該是currentThreadHasQueuedPredecessors()更爲妥帖一些,也就是說當前面沒有人排在該節點(Node)前面時候隊且能夠設置成功狀態,才能夠獲取鎖。

釋放語義:

01 protected final boolean tryRelease(int releases) {
02     int c = getState() - releases;
03     if (Thread.currentThread() != getExclusiveOwnerThread())
04         throw new IllegalMonitorStateException();
05     boolean free = false;
06     if (c == 0) {
07         free = true;
08         setExclusiveOwnerThread(null);
09     }
10     setState(c);
11     return free;
12 }

上述邏輯主要主要計算了釋放狀態後的值,如果爲0則完全釋放,返回true,反之僅是設置狀態,返回false。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章