要點(diǎn)解說
ReentrantLock是一個(gè)可重入的互斥鎖,它不但具有synchronized實(shí)現(xiàn)的同步方法和同步代碼塊的基本行為和語義簿透,而且具備很強(qiáng)的擴(kuò)展性。ReentrantLock提供了公平鎖和非公平鎖兩種實(shí)現(xiàn)解藻,在默認(rèn)情況下構(gòu)造的ReentrantLock實(shí)例是非公平鎖老充,可以在創(chuàng)建ReentrantLock實(shí)例的時(shí)候通過指定公平策略參數(shù)來指定是使用公平鎖還是非公平鎖。本篇將基于JDK7深入源碼解析非公平鎖的實(shí)現(xiàn)原理螟左。
實(shí)例演示
下面是使用ReentrantLock非公平鎖的典型代碼啡浊。
class X {
private final ReentrantLock lock = new ReentrantLock();
public void m() {
lock.lock();
try {
// do some thing
} finally {
lock.unlock()
}
}
}
方法解析
- ReentrantLock()創(chuàng)建一個(gè)非公平鎖ReentrantLock實(shí)例;
- ReentrantLock(boolean fair)根據(jù)公平策略fair參數(shù)創(chuàng)建ReentrantLock實(shí)例胶背;
- lock()獲取鎖巷嚣;
- unlock()釋放鎖;
- newCondition()返回與此ReentrantLock實(shí)例一起使用的Condition的實(shí)例钳吟;
- getHoldCount()獲取當(dāng)前線程持有此鎖的次數(shù)廷粒;
- getQueueLength()返回正在等待獲取此鎖的線程數(shù);
- getWaitQueueLength(Condition condition)返回等待與此鎖相關(guān)的給定條件的線程數(shù);
- hasQueuedThread(Thread thread)返回指定線程是否正在等待獲取此鎖坝茎;
- hasQueuedThreads()返回是否有線程正在等待獲取此鎖涤姊;
- hasWaiters(Condition condition)返回是否有線程正在等待與此鎖有關(guān)的給定條件;
- isFair()返回鎖是否是公平鎖嗤放;
- isHeldByCurrentThread()返回當(dāng)前線程是否持有此鎖思喊;
- tryLock()嘗試獲取鎖,僅在調(diào)用時(shí)鎖未被其它線程持有時(shí)才可以獲取該鎖次酌;
- tryLock(long timeout, TimeUnit unit)嘗試獲取鎖恨课,如果鎖在指定等待時(shí)間內(nèi)沒有被另一個(gè)線程持有,并且當(dāng)前線程未被中斷和措,則可以獲取該鎖庄呈。
源碼解析
從上面的方法解析可以看到它有很多方法,本文將重點(diǎn)深入分析lock()和unlock()方法派阱。首先诬留,從構(gòu)造函數(shù)開始。
//默認(rèn)構(gòu)造方法創(chuàng)建的是非公平鎖
public ReentrantLock() {
//構(gòu)造非公平鎖
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
//根據(jù)公平策略構(gòu)造公平鎖或非公平鎖
sync = fair ? new FairSync() : new NonfairSync();
}
從上面的代碼可以看到贫母,默認(rèn)構(gòu)造函數(shù)使用NonfairSync創(chuàng)建的是非公平鎖文兑,當(dāng)然也可以指定公平策略參數(shù)fair為false創(chuàng)建非公平鎖。NonfairSync是ReentrantLock中的靜態(tài)內(nèi)部類腺劣,它繼承了Sync绿贞,而Sync是ReentrantLock中的抽象靜態(tài)內(nèi)部類,Sync又繼承自AbstractQueuedSynchronizer橘原,分析到這里可以看到籍铁,ReentrantLock的具體實(shí)現(xiàn)使用了AQS。
abstract static class Sync extends AbstractQueuedSynchronizer {
//此處省略內(nèi)部代碼趾断,后面具體分析
}
static final class NonfairSync extends Sync{
//此處省略內(nèi)部代碼拒名,后面具體分析
}
當(dāng)調(diào)用lock()方法獲取鎖時(shí),具體實(shí)現(xiàn)代碼如下芋酌。
//ReentrantLock類的lock方法
public void lock() {
//此時(shí)的sync是NonfairSync的實(shí)例對(duì)象增显,所以執(zhí)行sync.lock()將執(zhí)行NonfairSync類的lock()方法
sync.lock();
}
//NonfairSync類的lock()方法
final void lock() {
//通過CAS設(shè)置AQS中state的值
//如果此時(shí)state值等于0,也就是鎖沒有被其它線程持有脐帝,則返回true
if (compareAndSetState(0, 1))
//設(shè)置獨(dú)占鎖的當(dāng)前所有者為當(dāng)前線程
setExclusiveOwnerThread(Thread.currentThread());
else
//否則同云,嘗試獲取獨(dú)占鎖
//acquire方法繼承自AbstractQueuedSynchronizer
acquire(1);
}
//AbstractQueuedSynchronizer類的acquire方法
public final void acquire(int arg) {
//調(diào)用tryAcquire方法嘗試獲取鎖,如果成功則返回堵腹,否則先執(zhí)行addWaiter方法炸站,再執(zhí)行acquireQueued方法
//因?yàn)楣芥i和非公平鎖對(duì)鎖持有的實(shí)現(xiàn)不同,所以這里的tryAcquire使用的是NonfairSync類中的實(shí)現(xiàn)
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//中斷當(dāng)前線程
selfInterrupt();
}
//NonfairSync類的tryAcquire方法
protected final boolean tryAcquire(int acquires) {
//nonfairTryAcquire方法繼承自Sync類
return nonfairTryAcquire(acquires);
}
//Sync類的nonfairTryAcquire方法
final boolean nonfairTryAcquire(int acquires) {
//獲取當(dāng)前線程
final Thread current = Thread.currentThread();
//獲取AQS中state當(dāng)前值
int c = getState();
//如果state當(dāng)前值等于0
if (c == 0) {
//使用CAS修改state值
//如果此時(shí)state值等于0疚顷,也就是鎖沒有被其它線程持有旱易,則修改成功
if (compareAndSetState(0, acquires)) {
//設(shè)置獨(dú)占鎖的當(dāng)前所有者為當(dāng)前線程
setExclusiveOwnerThread(current);
//獲取到鎖
return true;
}
}
//如果獨(dú)占鎖的當(dāng)前所有者是當(dāng)前線程,鎖重入的情況
else if (current == getExclusiveOwnerThread()) {
//將state值加一
int nextc = c + acquires;
//判斷新值是否溢出
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//更新state值
setState(nextc);
//獲取到鎖
return true;
}
//獲取不到鎖
return false;
}
//上面分析的代碼可以清晰的看到,當(dāng)鎖沒有被線程持有時(shí)的獲取過程咒唆,
//但是,如果鎖此時(shí)被其它線程持有释液,即執(zhí)行tryAcquire方法返回false全释,
//此時(shí)將需要先執(zhí)行addWaiter方法,將當(dāng)前線程封裝成Node節(jié)點(diǎn)误债,并將這個(gè)Node節(jié)點(diǎn)插入到同步等待隊(duì)列的尾部浸船,
//然后執(zhí)行acquireQueued方法,阻塞當(dāng)前線程寝蹈,具體實(shí)現(xiàn)代碼分析如下:
//addWaiter方法繼承自AQS
//將當(dāng)前線程封裝成Node節(jié)點(diǎn)李命,并將這個(gè)Node節(jié)點(diǎn)插入到同步等待隊(duì)列的尾部
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
//acquireQueued方法繼承自AQS
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//獲取當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)
final Node p = node.predecessor();
//如果當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)是頭結(jié)點(diǎn),并且可以獲取到鎖箫老,跳出循環(huán)并返回false
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)不是頭結(jié)點(diǎn)封字,或不可以獲取到鎖
//shouldParkAfterFailedAcquire方法檢查當(dāng)前節(jié)點(diǎn)在獲取鎖失敗后是否要被阻塞
//如果shouldParkAfterFailedAcquire方法執(zhí)行結(jié)果是當(dāng)前節(jié)點(diǎn)線程需要被阻塞,則執(zhí)行parkAndCheckInterrupt方法阻塞當(dāng)前線程
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
//parkAndCheckInterrupt方法繼承自AQS耍鬓,用于阻塞當(dāng)前線程
private final boolean parkAndCheckInterrupt() {
//阻塞當(dāng)前線程阔籽,當(dāng)前線程執(zhí)行到這里即被掛起,等待被喚醒
//當(dāng)當(dāng)前節(jié)點(diǎn)的線程被喚醒的時(shí)候牲蜀,會(huì)繼續(xù)嘗試獲取鎖
LockSupport.park(this);
return Thread.interrupted();
}
當(dāng)調(diào)用unlock()方法釋放持有的鎖時(shí)笆制,具體實(shí)現(xiàn)代碼如下。
//ReentrantLock類的unlock方法
public void unlock() {
//此時(shí)的sync是NonfairSync的實(shí)例對(duì)象
//在NonfairSync中沒有重寫release方法涣达,release方法繼承自AQS在辆,所以執(zhí)行AQS的release方法
sync.release(1);
}
//AQS的release方法
public final boolean release(int arg) {
//嘗試釋放持有的鎖
//如果釋放成功,則從同步等待隊(duì)列的頭結(jié)點(diǎn)開始喚醒等待線程
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//喚醒同步等待隊(duì)列中的阻塞線程
unparkSuccessor(h);
return true;
}
return false;
}
//嘗試釋放持有的鎖
protected final boolean tryRelease(int releases) {
//將state值減去1
int c = getState() - releases;
//如果當(dāng)前線程不是持有鎖的線程度苔,拋異常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//如果state的值等于零匆篓,則表明此鎖已經(jīng)完全被釋放
//如果state的值不等于零,則表明線程持有的鎖(可重入鎖)還沒有完全被釋放
if (c == 0) {
//free=true表示鎖以被完全釋放
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
//喚醒阻塞線程
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
//如果后繼節(jié)點(diǎn)為空或已被取消林螃,則從尾部開始找到等待隊(duì)列中第一個(gè)waitStatus<=0奕删,即未被取消的節(jié)點(diǎn)
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
//喚醒等待隊(duì)列節(jié)點(diǎn)中的線程
//之前執(zhí)行到parkAndCheckInterrupt方法的線程繼續(xù)執(zhí)行,再次嘗試獲取鎖
LockSupport.unpark(s.thread);
}
原理總結(jié)
A疗认、B兩個(gè)線程同時(shí)執(zhí)行l(wèi)ock()方法獲取鎖完残,假設(shè)A先執(zhí)行獲取到鎖,此時(shí)state值加1横漏,如果線程A在繼續(xù)執(zhí)行的過程中又執(zhí)行了lock()方法谨设,線程A會(huì)直接獲取鎖,同時(shí)state值加1缎浇,state的值可以簡(jiǎn)單理解為線程A執(zhí)行l(wèi)ock()方法的次數(shù)扎拣;當(dāng)線程B執(zhí)行l(wèi)ock()方法獲取鎖時(shí),會(huì)將線程B封裝成Node節(jié)點(diǎn),并將其插入到同步等待隊(duì)列的尾部二蓝,然后阻塞當(dāng)前線程誉券,等待被喚醒再次嘗試獲取鎖;線程A每次執(zhí)行unlock()方法都會(huì)將state值減1刊愚,直到state的值等于零則表示完全釋放掉了線程A持有的鎖踊跟,此時(shí)將從同步等待隊(duì)列的頭節(jié)點(diǎn)開始喚醒阻塞的線程,阻塞線程恢復(fù)執(zhí)行鸥诽,再次嘗試獲取鎖商玫。ReentrantLock非公平鎖的實(shí)現(xiàn)使用了AQS的同步等待隊(duì)列和state。
END
如果覺得有收獲牡借,記得關(guān)注拳昌、點(diǎn)贊、轉(zhuǎn)發(fā)钠龙。