ReentrantLock實(shí)現(xiàn)了Lock接口堆生,提供了lock虐译、trylock泉唁、unlock等方法鹅龄。這些方法通過(guò)AQS同步器來(lái)管理鎖狀態(tài),實(shí)現(xiàn)加鎖和解鎖亭畜。ReentrantLock包含幾個(gè)特性:公平鎖扮休、可重入、非阻塞獲取鎖拴鸵、可中斷等玷坠,下面來(lái)看看這些特性是如何實(shí)現(xiàn)的蜗搔。
先簡(jiǎn)單介紹下AQS,它是并發(fā)包里鎖管理的核心八堡,包含幾個(gè)重要屬性:
- state字段樟凄,鎖計(jì)數(shù)器來(lái)記錄鎖的狀態(tài)
- thread字段,擁有鎖的線(xiàn)程
- Node兄渺,Node包含pre和next指針缝龄,實(shí)現(xiàn)了CLH等待隊(duì)列
公平性
ReentrantLock中通過(guò)繼承Sync實(shí)現(xiàn)了公平(FairSync)和非公平(NonfairSync)兩種同步器。主要區(qū)別體現(xiàn)在lock方法上溶耘,lock方法會(huì)調(diào)用tryAcquire方法二拐,去獲取鎖。公平和非公平同步器獲取鎖的方式就相差hasQueuedPredecessors()這一個(gè)方法凳兵。
//公平鎖tryAcquire方法片段
if (c == 0) {
if (!hasQueuedPredecessors() && //非公平鎖沒(méi)有!hasQueuedPredecessors()這個(gè)條件
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
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());
}
hasQueuedPredecessors()方法是判斷百新,等待隊(duì)列上是否有非當(dāng)前線(xiàn)程符合鎖獲取條件。
所以公平鎖實(shí)現(xiàn)是先判斷鎖沒(méi)有被獲取庐扫,且等待隊(duì)列沒(méi)有其他 符合鎖獲取條件的線(xiàn)程在等待饭望,那么當(dāng)前線(xiàn)程才嘗試去獲取鎖。而非公平鎖是不管有沒(méi)有線(xiàn)程在等待形庭,都直接去嘗試獲取鎖铅辞。
符合鎖獲取條件:等待線(xiàn)程處于等待隊(duì)列的第二個(gè),第一個(gè)是代表當(dāng)前獲取鎖的線(xiàn)程萨醒,第二個(gè)代表下一個(gè)可以獲取鎖的線(xiàn)程
可重入
可重入性也是體現(xiàn)在加鎖的時(shí)候斟珊。加鎖時(shí),如果鎖已經(jīng)被占用富纸,當(dāng)前線(xiàn)程會(huì)判斷占有鎖的線(xiàn)程是不是自己囤踩。如果是那么再次進(jìn)入鎖,鎖計(jì)數(shù)器(state)加n晓褪。
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;
}
非阻塞獲取鎖
通過(guò)tryLock方法堵漱,可以實(shí)現(xiàn)非阻塞獲取鎖。獲取鎖時(shí)涣仿,如果鎖沒(méi)被占用勤庐,則通過(guò)compareAndSetState更新鎖狀態(tài)為1代表被占用,更新失敗再判斷是否可重入好港,都失敗那么直接返回愉镰,并不會(huì)阻塞等待。
如果是設(shè)置了超時(shí)的tryLock钧汹,在獲取鎖失敗后加入等待隊(duì)列岛杀,并通過(guò)LockSupport.parkNanos方法使線(xiàn)程進(jìn)入有限時(shí)間的阻塞,線(xiàn)程沒(méi)被喚醒或者阻塞時(shí)間到崭孤,則獲取鎖失敗类嗤。
可中斷
一般情況ReentrantLock在獲取鎖的時(shí)候糊肠,如果線(xiàn)程被中斷,中斷信息并不會(huì)被拋出來(lái)遗锣,如果通過(guò)tryInterruptibly方法獲取鎖货裹,就可以捕獲到中斷異常,終止鎖獲取行為精偿。
//tryInterruptibly實(shí)際調(diào)用該方法
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//獲取鎖失敗
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
//判斷當(dāng)前線(xiàn)程是否符合所獲取條件弧圆,嘗試獲取鎖
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
//拋出線(xiàn)程中斷異常
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
doAcquireInterruptibly方法先嘗試獲取鎖,如果獲取失敗笔咽,則進(jìn)入等待隊(duì)列搔预,線(xiàn)程等待被喚醒或者被中斷。如果是被中斷信息叶组,會(huì)重新拋出一個(gè)InterruptedException異常拯田,導(dǎo)致鎖獲取行為中斷。
關(guān)于鎖的釋放
ReentrantLock是可重入的獨(dú)占鎖甩十,對(duì)于鎖的釋放有點(diǎn)特殊船庇。每次重入鎖時(shí),都會(huì)在同步器的計(jì)數(shù)器上加1侣监,而調(diào)用unlock釋放鎖時(shí)鸭轮,是對(duì)計(jì)數(shù)器減1,所以重入多次數(shù)和釋放次數(shù)要一樣橄霉,計(jì)數(shù)器為0后鎖才能被其他線(xiàn)程使用窃爷。
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
//如果鎖釋放成功,就更新head節(jié)點(diǎn)狀態(tài)姓蜂,并喚醒下一個(gè)node
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//計(jì)數(shù)器為0時(shí)按厘,返回值free才成功
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}