參考鏈接: https://www.bilibili.com/video/BV1ta4y1H73X
需要具備 AQS 知識(shí)
可重入指的是單個(gè)線程執(zhí)行時(shí)重新進(jìn)入同一個(gè)子程序仍是線程安全的薛匪。
如果是不可重入室叉,若 A 獲得鎖,要再次請(qǐng)求該鎖時(shí)就會(huì)造成死鎖
簡(jiǎn)單來(lái)說(shuō)妒貌,就是一個(gè)線程可以不用釋放即可重復(fù)獲得該鎖 n 次,釋放時(shí)響應(yīng)釋放 n 次。
那下面就來(lái)講講 RenentrantLocak 這一可重入鎖的實(shí)現(xiàn)。
首先來(lái)看一下 RenentrantLocak 的繼承關(guān)系均蜜,其實(shí)現(xiàn)了 Lock 接口,即遵循 Lock 接口的抽象定義芒率。
public class ReentrantLock implements Lock, java.io.Serializable
Lock
Lock 提供了區(qū)別于 synchronized 的另一種同步操作方式囤耳。
操作更廣泛,能支持更多靈活的結(jié)構(gòu)敲董,并且可以關(guān)聯(lián)多個(gè) Condition 對(duì)象紫皇。
// 獲取鎖
void lock();
// 獲取鎖,若在等待鎖的過(guò)程中被打斷腋寨,則退出等待并拋出異常
void lockInterruptibly() throws InterruptedException;
// 嘗試獲取鎖聪铺,并立即返回
boolean tryLock();
// 在一段時(shí)間內(nèi)嘗試獲取鎖,如果期間被中斷萄窜,則會(huì)拋出中斷異常
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 釋放鎖
void unlock();
// 新建一個(gè)綁定在當(dāng)前 Lock 上的 Condition 對(duì)象
Condition newCondition();
公平鎖 與 非公平鎖
公平鎖:按照鎖的請(qǐng)求順序進(jìn)行分配铃剔,如 AQS 中的 FIFO 機(jī)制實(shí)現(xiàn)的就是公平鎖
非公平鎖:不按鎖的請(qǐng)求順序進(jìn)行分配,不過(guò)這種方式可能存在 饑餓 現(xiàn)象
為什么設(shè)計(jì)非公平鎖呢查刻?
實(shí)際上键兜,非公平鎖的效率往往更高,可能能提高并發(fā)的性能穗泵。
當(dāng)喚醒線程時(shí)普气,線程狀態(tài)切換會(huì)有短暫的延時(shí),非公平鎖機(jī)制允許線程在這段事件進(jìn)行鎖的搶占佃延,快速處理內(nèi)容现诀。
這是非公平鎖比公平鎖性能更好的原因之一。
構(gòu)造方法
從構(gòu)造方法上看履肃,ReentrantLock 實(shí)際上是 Sync 類仔沿,可以通過(guò)參數(shù)對(duì) FairSync 和 NonfairSync(默認(rèn))進(jìn)行選擇。
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
屬性
final Sync sync
Sync 是 ReentrantLock 內(nèi)部抽象類尺棋,繼承了 AQS.
NonfairSync 和 FairSync 是 Sync 實(shí)現(xiàn)子類封锉,分別是對(duì) 非公平鎖 和 公平鎖 的實(shí)現(xiàn)。
abstract static class Sync extends AbstractQueuedSynchronizer {...}
static final class NonfairSync extends Sync {...}
static final class FairSync extends Sync {...}
回到 Sync, 留意到 Sync 中存在nonfairTryAcquire(int acquires)
方法成福,通過(guò)名稱可以明白其是非公平鎖的方法碾局,既然由 NonfairSync 和 FairSync 的實(shí)現(xiàn),為何該方法會(huì)在其父類中定義呢奴艾?
下面就來(lái)看看該方法:
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 0 表示鎖狀態(tài)空閑擦俐,則可以進(jìn)行 CAS 來(lái)獲取鎖
// 若 state 更改成功,則獲取到該鎖
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 若鎖被占用握侧,判斷當(dāng)前線程是否是持有鎖的線程
else if (current == getExclusiveOwnerThread()) {
// 重入,則給計(jì)數(shù) +1
int nextc = c + acquires;
if (nextc < 0) // overflow嘿期,即防止線程數(shù)量溢出后變?yōu)樨?fù)數(shù)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
// 未獲取到鎖
return false;
}
NonfairSync
NonfairSync 通過(guò)提供兩次搶占實(shí)現(xiàn)非公平鎖機(jī)制:
- lock 時(shí)進(jìn)行搶占
- 重寫的 tryAcquire 調(diào)用的 nonfairTryAcquire
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
// 嘗試對(duì)鎖進(jìn)行獲取 [第一次搶占]
if (compareAndSetState(0, 1))
// 成功則獲取到鎖
setExclusiveOwnerThread(Thread.currentThread());
else
// 失敗則調(diào)用 AQS 的 acquire 方法
// 由于 acquire 方法中調(diào)用的 tryAcquire 被重寫
// 因此是調(diào)用下方的 trayAcquire 方法 [第二次搶占]
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
FairSync
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
// 直接進(jìn)行入隊(duì)操作
acquire(1);
}
// 重寫 tryAcquire
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 判斷鎖是否空閑
if (c == 0) {
// 需要判斷是否有前置等待節(jié)點(diǎn)
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 重入機(jī)制
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
方法
lock
在 sync 中已實(shí)現(xiàn)品擎,直接調(diào)用即可。
public void lock() {
sync.lock();
}
trylock
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
unlock
public void unlock() {
sync.release(1);
}