讀本篇前,一定要確保已經讀過本公眾號的AQS講解。
我們知道實現一把鎖要有如下幾個邏輯
我們在講解AQS的時候說過AQS基本負責了實現鎖的全部邏輯,唯獨線程搶鎖和線程釋放鎖的邏輯是交給子類來實現了,而ReentrantLock作為最常用的獨占鎖,其內部就是包含了AQS的子類實現了線程搶鎖和釋放鎖的邏輯。
我們在使用ReentrantLock的時候一般只會使用如下方法:
ReentrantLock lock=new ReentrantLock(); lock.lock(); lock.unlock(); lock.tryLock(); Condition condition=lock.newCondition(); condition.await(); condition.signal(); condition.signalAll();
如果我們自己來實現一個鎖,那么如何設計呢?
根據AQS的邏輯,我們寫一個子類sync,這個類一定會調用父類的acquire方法進行上鎖,同時重寫tryAcquire方法實現自己搶鎖邏輯,也一定會調用release方法進行解鎖,同時重寫tryRelease方法實現釋放鎖邏輯。
圖片
那么ReentrantLock是怎么實現的呢?
ReentrantLock的實現的類架構如下,ReentrantLock對外提供作為一把鎖應該具備的api,比如lock加鎖,unlock解鎖等等,而它內部真正的實現是通過靜態內部類sync實現,sync是AQS的子類,是真正的鎖,因為這把鎖需要支持公平和非公平的特性,所以sync又有兩個子類FairSync和NonfairSync分別實現公平鎖和非公平鎖。
圖片
因為是否公平說的是搶鎖的時候是否公平,那兩個子類就要在上鎖方法acquire的調用和搶鎖方法tryAcquire的重寫上做文章。
公平鎖做了什么文章?
static final class FairSync extends Sync { 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; } }
公平鎖比較簡單,直接調用了父級類AQS的acquire方法,因為AQS的鎖默認就是公平的排隊策略。
重寫tryAcquire方法的邏輯為:
公平就公平在老老實實排隊。
非公平鎖做了什么文章?
static final class NonfairSync extends Sync { 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; }
非公平鎖也很簡單,沒有直接調用了父級類AQS的acquire方法,而是先通過cas搶鎖,它不管等待隊列中有沒有其他線程在排隊,直接搶鎖,這就體現了不公平。
它重寫tryAcquire方法的邏輯為:
公平鎖和非公平分別重寫了tryAcquire方法,來滿足公平和非公平的特性。那么tryAcquire方法也是需要子類重寫的,因為它和是否公平無關,因此tryAcquire方法被抽象到sync類中重寫。
sync類中protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; }
釋放鎖的邏輯如下:
釋放鎖往往和搶鎖邏輯是對應的,每個子類搶鎖邏輯不同的話,釋放鎖的邏輯也會對應不同。
接下來我們通過ReentrantLock的使用看下它的源碼實現
class X { private final ReentrantLock lock = new ReentrantLock(); Condition condition1=lock.newCondition(); Condition condition2=lock.newCondition(); public void m() { lock.lock(); try { if(條件1){ condition1.await(); } if(條件2){ condition2.await(); } } catch (InterruptedException e) { } finally { condition1.signal(); condition2.signal(); lock.unlock(); } } }
ReentrantLock類public void lock() { sync.lock(); }
NonfairSync 類中 final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
FairSync 類中 final void lock() { acquire(1); }
公平鎖和非公平鎖中都實現了lock方法,公平鎖直接調用AQS的acquire,而非公平鎖先搶鎖,搶不到鎖再調用AQS的acquire方法進行上鎖
進入acquire方法后的邏輯我們就都知道了。
public void unlock() { sync.release(1);}
unlock方法內直接調用了AQS的Release方法進行解鎖的邏輯,進入release方法后邏輯我們都已經知道了,這里不再往下跟。
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); }
tryLock方法直接調用sync的tryAcquireNanos方法,看過AQS的應該知道tryAcquireNanos這個方法是父類AQS的方法,這個方法和AQS中的四個核心方法中的Acquire方法一樣都是上鎖的方法,無非是上鎖的那幾個步驟,調用tryAcquire方法嘗試搶鎖,搶不到鎖就會進入doAcquireNanos方法。
public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); return tryAcquire(arg) || doAcquireNanos(arg, nanosTimeout); }
doAcquireNanos這個方法做的其實就是入隊,阻塞等一系列上鎖操作,邏輯和Acquire方法中差不多,但是有兩點不同:
看下下面的代碼
private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { if (nanosTimeout <= 0L) return false; final long deadline = System.nanoTime() + nanosTimeout; final Node node = addWaiter(Node.EXCLUSIVE); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return true; } nanosTimeout = deadline - System.nanoTime(); if (nanosTimeout <= 0L) return false; if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold) LockSupport.parkNanos(this, nanosTimeout); if (Thread.interrupted()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
這里的阻塞不再是LockSupport類的park方法,而是parkNanos方法,這個方法支持指定時長的阻塞,AQS正是利用這個方法實現阻塞指定時長,當自動喚醒后,循環中會判斷是否超過設定時長,如果超過直接返回false跳出循環。
在阻塞期間,如果線程被中斷,就會拋出異常,同樣會跳出循環,外面可以通過捕獲這個異常達到中斷阻塞的目的。
可見ReentrantLock其實啥也沒做,其tryLock方法完全是依賴AQS實現。
在AQS那篇我們說過Condition是AQS中的條件隊列,可以按條件將一批線程由不可喚醒變為可喚醒。
ReentrantLock類 public Condition newCondition() { return sync.newCondition(); }
sync靜態內部類final ConditionObject newCondition() { return new ConditionObject();}
sync提供了創建Condition對象的方法,意味著ReentrantLock也擁有Condition的能力。
我們下面說的ReentrantLock其實就是說AQS,因為它的同步實現主要在AQS里面。
synchronized不需要手動釋放鎖,ReentrantLock需要手動釋放鎖,需要考慮異常對釋放鎖的影響避免異常導致線程一直持有鎖。
以下是兩個鎖的使用方式
class X { private final ReentrantLock lock = new ReentrantLock(); Condition condition1=lock.newCondition(); Condition condition2=lock.newCondition(); public void m() { lock.lock(); try { if(1==2){ condition1.await(); } if(1==3){ condition2.await(); } } catch (InterruptedException e) { } finally { condition1.signal(); condition2.signal(); lock.unlock(); } } }
class X { private final testtest sync=new testtest();; public void m() throws InterruptedException { synchronized(sync){ if(1==2){ sync.wait(); } sync.notify(); sync.notifyAll(); } } }
對比代碼及特性說明:
本文鏈接:http://www.tebozhan.com/showinfo-26-13582-0.html面試中如何答好:ReentrantLock
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: Java中的Volatile到底是什么?