ReentrantLock的加鎖和解鎖原理
阿新 • • 發佈:2019-01-01
ReentrantLock的實現依賴於Java同步器框架AQS。
AQS使用一個整型的votalie變數(命名為state)來維護同步狀態。
- ReentrantLock分為公平鎖和非公平鎖
公平鎖
使用公平鎖時,加鎖方法lock()
的呼叫軌跡:
- ReentrantLock:lock()
- FairSync:lock()
- AbstractQueuedSynchronizer:acquire(int arg)
- ReentrantLock:tryAcquire(int acquires)
第4步開始加鎖,原始碼如下
protected final boolean tryAcquire(int acquires) { //獲取當前執行緒 final Thread current = Thread.currentThread(); //通過AQS獲取同步狀態 int c = getState(); //同步狀態為0,說明臨界區處於無鎖狀態, if (c == 0) { //判斷該執行緒是否在隊首,然後修改同步狀態,即加鎖 if (isFirst(current)&&compareAndSetState(0, acquires)) { //將當前執行緒設定為鎖的owner 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; }
其解鎖方法unlock()
呼叫軌跡如下:
- ReentrantLock:unlock()
- AbstractQueuedSynchronizer:release(int arg)
- Sync:tryRelease(int releases)
第3步才開始真正釋放鎖
protected final boolean tryRelease(int releases){ int c = getState() - releases; if(Tread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException() boolean free = false; if(c == 0){//如果state減去releases為0,則釋放鎖 free = true; setExcluiveOwnerThread(null);//將獲取鎖的執行緒設為null,即無鎖 } setState(c);//設定state,若c為0,則釋放鎖 return free; }
非公平鎖
非公平鎖的釋放與公平鎖一樣,這裡僅探討鎖的獲取。
使用非公平鎖時,加鎖方法lock()呼叫軌跡如下:
- ReentrantLock:lock()
- NonfairSync:lock()
- AbstractQueuedSynchronizer:compareAndSetState(int expect,int update)
static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; final void lock() { if (compareAndSetState(0, 1))//對state進行CAS //若成功則加鎖 setExclusiveOwnerThread(Thread.currentThread(); else acquire(1); } }
第3步進行加鎖。
該方法使用原子操作的方式更新state變數。
總結:
- 公平鎖和非公平鎖的釋放時,最後都要寫一個volatile變數state。
- 公平鎖獲取時,首先會讀volatile變數。
- 非公平鎖的獲取,首先會用CAS更新volatile變數,這個操作同時具有volatlie寫和volatile讀的記憶體語義。