掃二維碼與項目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
我們知道實現(xiàn)一把鎖要有如下幾個邏輯:

我們在講解AQS的時候說過AQS基本負責(zé)了實現(xiàn)鎖的全部邏輯,唯獨線程搶鎖和線程釋放鎖的邏輯是交給子類來實現(xiàn)了,而ReentrantLock作為最常用的獨占鎖,其內(nèi)部就是包含了AQS的子類實現(xiàn)了線程搶鎖和釋放鎖的邏輯。
我們在使用ReentrantLock的時候一般只會使用如下方法:
ReentrantLock lock=new ReentrantLock();
lock.lock();
lock.unlock();
lock.tryLock();
Condition condition=lock.newCondition();
condition.await();
condition.signal();
condition.signalAll();
如果我們自己來實現(xiàn)一個鎖,那么如何設(shè)計呢?
根據(jù)AQS的邏輯,我們寫一個子類sync,這個類一定會調(diào)用父類的acquire方法進行上鎖,同時重寫tryAcquire方法實現(xiàn)自己搶鎖邏輯,也一定會調(diào)用release方法進行解鎖,同時重寫tryRelease方法實現(xiàn)釋放鎖邏輯。
那么ReentrantLock是怎么實現(xiàn)的呢?
ReentrantLock的實現(xiàn)的類架構(gòu)如下,ReentrantLock對外提供作為一把鎖應(yīng)該具備的api,比如lock加鎖,unlock解鎖等等,而它內(nèi)部真正的實現(xiàn)是通過靜態(tài)內(nèi)部類sync實現(xiàn),sync是AQS的子類,是真正的鎖,因為這把鎖需要支持公平和非公平的特性,所以sync又有兩個子類FairSync和NonfairSync分別實現(xiàn)公平鎖和非公平鎖。
因為是否公平說的是搶鎖的時候是否公平,那兩個子類就要在上鎖方法acquire的調(diào)用和搶鎖方法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;
}
}公平鎖比較簡單,直接調(diào)用了父級類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;
}非公平鎖也很簡單,沒有直接調(diào)用了父級類AQS的acquire方法,而是先通過cas搶鎖,它不管等待隊列中有沒有其他線程在排隊,直接搶鎖,這就體現(xiàn)了不公平。
它重寫tryAcquire方法的邏輯為:
公平鎖和非公平分別重寫了tryAcquire方法,來滿足公平和非公平的特性。那么tryAcquire方法也是需要子類重寫的,因為它和是否公平無關(guān),因此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;
}釋放鎖的邏輯如下:
釋放鎖往往和搶鎖邏輯是對應(yīng)的,每個子類搶鎖邏輯不同的話,釋放鎖的邏輯也會對應(yīng)不同。
接下來我們通過ReentrantLock的使用看下它的源碼實現(xiàn):
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);
}公平鎖和非公平鎖中都實現(xiàn)了lock方法,公平鎖直接調(diào)用AQS的acquire,而非公平鎖先搶鎖,搶不到鎖再調(diào)用AQS的acquire方法進行上鎖
進入acquire方法后的邏輯我們就都知道了。
public void unlock() {
sync.release(1);
}unlock方法內(nèi)直接調(diào)用了AQS的Release方法進行解鎖的邏輯,進入release方法后邏輯我們都已經(jīng)知道了,這里不再往下跟。
這個方法中直接調(diào)用了sync中的nonfairTryAcquire方法,這個就是非公平鎖實現(xiàn)的TryAcquire方法,只是名字改了而已。
tryLock方法的意思是嘗試搶鎖,是給程序員調(diào)用的嘗試搶鎖方法,如果搶鎖成功返回true,搶鎖失敗返回false,當(dāng)拿到搶鎖失敗的信號后,程序員可以做一些自己的策略。
如果沒有tryLock方法,而是直接調(diào)用lock方法,就會走到acquire方法中,Acquire方法中也會調(diào)用TryAcquire方法,只不過在這里如果獲取不到鎖就會入隊阻塞了,就沒有程序員參與的可能了。
在AQS那篇我們說過Condition是AQS中的條件隊列,可以按條件將一批線程由不可喚醒變?yōu)榭蓡拘选?/p>
ReentrantLock類
public Condition newCondition() {
return sync.newCondition();
}
sync靜態(tài)內(nèi)部類
final ConditionObject newCondition() {
return new ConditionObject();
}sync提供了創(chuàng)建Condition對象的方法,意味著ReentrantLock也擁有Condition的能力。
我們下面說的ReentrantLock其實就是說AQS,因為它的同步實現(xiàn)主要在AQS里面。
優(yōu)化前的synchronized性能很差,主要表現(xiàn)在兩個方面:
ReentrantLock是在jdk實現(xiàn)的,它申請互斥量就是對鎖標(biāo)識state的爭奪,它是通過cas方式實現(xiàn)。在java端實現(xiàn)。
對于爭奪不到資源的線程依然要阻塞掛起,但凡阻塞掛起都要依賴于操作系統(tǒng)底層,這一步的用戶態(tài)到內(nèi)核態(tài)的切換是避免不了的。
因此在單線程進入代碼塊的時候,效率是很高的,因此我們說ReentrantLock性能高于原始的synchronized:
兩個都是常用的典型的獨占鎖。
synchronized不需要手動釋放鎖,ReentrantLock需要手動釋放鎖,需要考慮異常對釋放鎖的影響避免異常導(dǎo)致線程一直持有鎖。
以下是兩個鎖的使用方式:
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();
}
}
}對比代碼及特性說明:

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流