ReentrantLock鎖的實現是基於AQS實現的,所以先簡單說下AQS:
AQS是AbstractQueuedSynchronizer縮寫,顧名思義:抽象的佇列同步器,它是JUC裡面許多同步工具類實現的核心
其實簡單來說AQS有兩個核心,一個是volatile修飾的int型別state,這個是記錄處於等待中需要持有鎖和正在持有鎖的執行緒數量
/** * The synchronization state. */ private volatile int state;
第二個就是Node內部類,他是AQS裡面FIFO雙向佇列節點的實現,他的一些屬性如下:
static final class Node {
volatile int waitStatus;//等待狀態 volatile Node prev;//前驅節點 volatile Node next;//後繼節點 volatile Thread thread;//申請同步狀態的執行緒 Node nextWaiter;//等待節點的後繼節點(後續出AQS詳細篇再細講) }
這種結構可以從任意的一個節點開始很方便的訪問前驅和後繼節點。每個Node由執行緒封裝,當執行緒競爭失敗後會加入到AQS佇列中去。
基於這些再繼續聊下ReentrantLock的公平和非公平鎖的實現
ReentrantLock鎖的實現基於AQS,如下sync抽象內部類:
abstract static class Sync extends AbstractQueuedSynchronizer{ abstract void lock(); }
Sync的子類有兩個:FairSync和NonfairSync(公平和非公平鎖的具體實現類),兩個鎖的不同就在於兩個方法的實現不同lock():加鎖的方法,還有tryAcquire(int acquires):嘗試持有鎖的方法,獲取成功返回true,失敗返回false
看看兩把鎖的原始碼實現:
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } }
static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } } nonfairTryAcquire方法是在父類Sync中定義: final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
可以看見,NonfairSync在使用lock()加鎖的時候就已經體現了非公平性了,因為lock()加鎖的時候直接嘗試使用CAS獲取鎖,如果獲取到了就不會入等待佇列,所以會有後來的執行緒先搶佔到鎖;
那如果沒有搶佔到鎖呢?會走else的acquire(1)方法。
先看看acquire方法的定義:
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
這裡會發現acquire呼叫了tryAcquire方法,而在上面兩把鎖的原始碼中各自重寫了tryAcquire方法
這個方法中當getState()獲取同步狀態為0(沒有執行緒持有鎖)時候,繼續使用用CAS獲取鎖
兩個重寫的方法唯一不同之處在於,公平鎖加了hasQueuedPredecessors()方法:
public final boolean hasQueuedPredecessors() { Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
這個方法主要是用來判斷執行緒需不需要排隊,因為佇列是FIFO的,所以需要判斷佇列中有沒有相關執行緒的節點已經在排隊了。有則返回true表示執行緒需要排隊,沒有則返回false則表示執行緒無需排隊。
而非公平鎖就沒有判斷FIFO佇列是否還有排隊。
小結:
ReentrantLock中公平鎖和非公平鎖的實現主要有兩處區別:
1.在lock()方法中,非公平鎖一進來就嘗試CAS獲取鎖,不會管等待佇列裡面是否有等待執行緒
2.在tryAcquire方法中,判斷state等於0(沒有執行緒持有鎖的情況)後,公平鎖會先呼叫hasQueuedPredecessors()方法判斷FIFO佇列是否有等待執行緒,沒有才繼續嘗試獲取鎖,而非公平鎖是直接CAS獲取鎖
值得注意的是眾所周知ReentrantLock是可重入鎖:
不管是公平鎖還是非公平鎖,他們重寫的嘗試搶佔鎖的tryAcquire方法裡面都有這樣一段程式碼
else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; }
這個是判斷state不等於0(有執行緒持有鎖或者等待執行緒時候)之後,繼續判斷當前持有鎖的執行緒是不是等於當前申請鎖的執行緒,如果等於,直接返回true並且state+1,所以當前執行緒不用等待繼續持有鎖;這就完成了可重入的特性!