初識(shí)
ReentrantLock在并發(fā)場(chǎng)景下經(jīng)常用到觅捆,非常重要铺遂。它是對(duì)lock接口實(shí)現(xiàn)的可重入鎖译秦,保證多線(xiàn)程安全满着。作用是對(duì)線(xiàn)程進(jìn)行加鎖叭披,并且鎖可重入袄简,即一個(gè)線(xiàn)程獲取鎖后可重復(fù)獲取鎖而不會(huì)發(fā)生阻塞歹鱼,其定義的內(nèi)部類(lèi)Sync繼承AQS抽象類(lèi)酪刀。通過(guò)AQS抽象類(lèi)實(shí)現(xiàn)大部分的功能谈喳,小部分功能由Sync類(lèi)實(shí)現(xiàn)册烈,如AQS類(lèi)里的tryAcquire()由子類(lèi)nonfairTryAcquire()實(shí)現(xiàn)等等
基本使用
public static ReentrantLock lock = new ReentrantLock();
private static void add() {
try {
lock.lock();
count++;
}finally {
lock.unlock();
}
}
使用new ReentrantLock即可進(jìn)行實(shí)例,對(duì)需要線(xiàn)程安全部分首尾調(diào)用lock()進(jìn)行加鎖,業(yè)務(wù)代碼執(zhí)行完調(diào)用lock.unlock()釋放鎖婿禽,最好是在finally中調(diào)用解鎖赏僧,以防業(yè)務(wù)代碼出現(xiàn)問(wèn)題,鎖不釋放帶來(lái)的死鎖問(wèn)題谈宛。
源碼解析
1.核心是AQS這個(gè)類(lèi)次哈,構(gòu)造一個(gè)CLH隊(duì)列,結(jié)構(gòu)如圖
2.ReentrantLock實(shí)現(xiàn)了Lock吆录,以及Serializable接口窑滞,提供了兩個(gè)構(gòu)造方法,其中無(wú)參構(gòu)造即實(shí)例一個(gè)非公平鎖恢筝,有參構(gòu)造方法里如果是true則構(gòu)造公平鎖
非公平鎖哀卫,當(dāng)前線(xiàn)程釋放鎖后,后續(xù)線(xiàn)程無(wú)需順序競(jìng)爭(zhēng)鎖撬槽。
公平鎖此改,當(dāng)前線(xiàn)程釋放鎖后,后續(xù)線(xiàn)程按創(chuàng)建時(shí)間競(jìng)爭(zhēng)鎖侄柔。
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
3.當(dāng)直接new一個(gè)lock實(shí)例即創(chuàng)建一個(gè)非公平鎖
/**
* Sync object for non-fair locks
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
NonfairSync類(lèi)是Sync抽象類(lèi)的一個(gè)子類(lèi)共啃,Sync下有兩個(gè)子類(lèi)占调,分別對(duì)應(yīng)公平和非公平,同時(shí)繼承父類(lèi)AbstractQueuedSynchronizer也就是AQS移剪,AQS類(lèi)完成加鎖以及阻塞隊(duì)列構(gòu)建和運(yùn)轉(zhuǎn)
關(guān)系圖
Sync抽象類(lèi)里定義的幾個(gè)方法究珊,
其中l(wèi)ock方法由兩個(gè)類(lèi)子類(lèi)FairSync和NonFairSync實(shí)現(xiàn),
nonfairRtyAcquire方法即為獲取鎖的實(shí)現(xiàn)被兩個(gè)子類(lèi)調(diào)用纵苛,后面再說(shuō)剿涮。
4.NonfairSync子類(lèi)的lock()方法實(shí)現(xiàn)(非公平鎖)
final void lock() {
//4.1.首先邏輯分支語(yǔ)句,第一個(gè)分支使用Sync的父類(lèi)AQS類(lèi)里的compareAndSetState()方法來(lái)獲取鎖攻人。
//AQS類(lèi)里定義一個(gè)volatile修飾的變量state;用來(lái)標(biāo)識(shí)當(dāng)前鎖是否被占用取试,當(dāng)沒(méi)有其他線(xiàn)程獲取鎖時(shí)將state值變?yōu)?,并且調(diào)用AQS的setExclusiveOwnerThread()方法將線(xiàn)程所有者設(shè)置為當(dāng)前線(xiàn)程怀吻。
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//4.2.如果獲取鎖沒(méi)有成功調(diào)用AQS的acquire(1)方法瞬浓。
acquire(1);
}
5.繼上一步調(diào)用AQS的acquire(1)方法,邏輯控制語(yǔ)句里有兩個(gè)部分烙博,都為true時(shí)瑟蜈,即線(xiàn)程已經(jīng)經(jīng)歷過(guò)一個(gè)完整的掛起到喚醒過(guò)程,然后再調(diào)用AQS的selfInterrupt()方法對(duì)當(dāng)前線(xiàn)程進(jìn)行標(biāo)識(shí)中斷(其實(shí)是對(duì)下面acquireQueued方法的一個(gè)中斷補(bǔ)償)渣窜,具體何時(shí)中斷由線(xiàn)程自己決定铺根。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
5.1.第一部分:AQS類(lèi)里的tryAcquire()方法調(diào)用子類(lèi)Sycn的nonfairTryAcquire()方法,入?yún)?乔宿。
nonfairTryAcquire()方法有三個(gè)分支位迂。
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//第一個(gè)分支,依舊是使用cas原理獲取鎖详瑞,如果獲取成功設(shè)置所屬線(xiàn)程掂林,返回true
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//第二個(gè)分支,則是ReenTrantLock的可重入性坝橡,即如果當(dāng)前線(xiàn)程和所屬線(xiàn)程一致泻帮,則將state值加1并且返回true。
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//第三個(gè)分支计寇,當(dāng)上面都不能成立直接返回false锣杂。
return false;
}
5.2.第二部分:acquireQueued(addWaiter(Node.EXCLUSIVE), arg)首先調(diào)用addWaiter()方法,將當(dāng)前線(xiàn)程封裝成node加入到隊(duì)列中
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// 判斷尾結(jié)點(diǎn)是否存在番宁,如果存在將新節(jié)點(diǎn)插入到尾結(jié)點(diǎn)元莫,將尾結(jié)點(diǎn)指向修改為新節(jié)點(diǎn)。
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//如果不存在調(diào)用enq(node)方法
enq(node);
return node;
}
//將node設(shè)置為tail尾結(jié)點(diǎn)蝶押,此時(shí)head頭節(jié)點(diǎn)是null
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
里面addwaiter方法執(zhí)行完踱蠢,接下來(lái)是acquireQueued(node,1)方法
final boolean acquireQueued(final Node node, int arg) {
//失敗標(biāo)記
boolean failed = true;
try {
//中斷標(biāo)記
boolean interrupted = false;
//自旋獲取鎖,失敗則掛起嘗試獲取鎖的線(xiàn)程(我稱(chēng)之為競(jìng)爭(zhēng)線(xiàn)程)棋电,直到被喚醒
for (;;) {
//獲取前驅(qū)節(jié)點(diǎn)
final Node p = node.predecessor();
//如果前驅(qū)節(jié)點(diǎn)是頭節(jié)點(diǎn)茎截,則嘗試獲取鎖
if (p == head && tryAcquire(arg)) {
//將當(dāng)前節(jié)點(diǎn)設(shè)置為頭節(jié)點(diǎn)
setHead(node);
//釋放內(nèi)存苇侵,幫助垃圾回收
p.next = null; // help GC
failed = false;
//競(jìng)爭(zhēng)線(xiàn)程獲取鎖后,返回false企锌,結(jié)束循環(huán)
return interrupted;
}
//下面這段主要是競(jìng)爭(zhēng)線(xiàn)程獲取鎖失敗時(shí)執(zhí)行衅檀,if邏輯里有兩個(gè)重要方法主要實(shí)現(xiàn)線(xiàn)程阻塞,在5.2.x里分析
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
5.2.1.shouldParkAfterFailedAcquire()方法主要是用來(lái)修正當(dāng)前節(jié)點(diǎn)以及前驅(qū)節(jié)點(diǎn)的狀態(tài)霎俩,并判斷當(dāng)前node是否可以進(jìn)行安全阻塞。如果可以安全阻塞則返回true繼續(xù)調(diào)用parkAndCheckInterrupt()進(jìn)行阻塞中斷沉眶。
volatile int waitStatus; //node節(jié)點(diǎn)狀態(tài)打却,默認(rèn)是0
static final int CANCELLED = 1 // waitStatus值,指示線(xiàn)程已取消
static final int SIGNAL = -1 // waitStatus值谎倔,指示后續(xù)線(xiàn)程需要喚醒
static final int CONDITION = -2 // waitStatus值柳击,指示線(xiàn)程正在等待條件
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
//如果前驅(qū)節(jié)點(diǎn)的狀態(tài)是-1,返回true片习,表示當(dāng)前node可以?huà)炱鸢齐龋瓷厦?.2可調(diào)用parkAndCheckInterrupt()
if (ws == Node.SIGNAL)
return true;
//如果前驅(qū)節(jié)點(diǎn)狀態(tài)>0,則表示前驅(qū)節(jié)點(diǎn)已經(jīng)取消藕咏,下面do while循環(huán)找到前驅(qū)節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)状知,并將它設(shè)置為node的前驅(qū)節(jié)點(diǎn)
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//如果不等于-1,且>0,則將前驅(qū)節(jié)點(diǎn)的狀態(tài)改為-1孽查。這段方法第一次進(jìn)來(lái)會(huì)執(zhí)行此處饥悴。注意此時(shí)并沒(méi)有去返回true來(lái)掛起當(dāng)前node,而是返回false盲再。
//因?yàn)榍膀?qū)節(jié)點(diǎn)狀態(tài)默認(rèn)是0西设。此方法外部自旋第二次循環(huán)才走第一個(gè)if返回true,然后開(kāi)始調(diào)用parkAndCheckInterrupt()掛起當(dāng)前node答朋,即掛起競(jìng)爭(zhēng)線(xiàn)程
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
5.2.2.parkAndCheckInterrupt()只做兩件事 1掛起 2檢查中斷狀態(tài)贷揽,這里特別說(shuō)明下,線(xiàn)程掛起和線(xiàn)程中斷不是一回事梦碗,此方法掛起線(xiàn)程后禽绪,代碼將不再繼續(xù)執(zhí)行,直到線(xiàn)程被中斷叉弦,或者被其他線(xiàn)程進(jìn)行unpark喚醒丐一,才能繼續(xù)return。
private final boolean parkAndCheckInterrupt() {
//掛起競(jìng)爭(zhēng)線(xiàn)程淹冰。
LockSupport.park(this);
//如果是被unpark喚醒库车,即返回false,如果是被中斷喚醒樱拴,返回true柠衍,重置競(jìng)爭(zhēng)線(xiàn)程中斷狀態(tài)為false洋满。
return Thread.interrupted();
}
繼續(xù)parkAndCheckInterrupt()返回后進(jìn)行分析。兩種情況:
第一種情況珍坊,通過(guò)unpark方法喚醒牺勾,return Thread.interrupted() => false,進(jìn)入acquireQueued()的自旋中阵漏,進(jìn)行再次獲取鎖操作后驻民。此時(shí)return的interrupted標(biāo)識(shí)還是false,結(jié)束acquireQueued()后履怯,第5步分析的selfInterrupt()的邏輯判斷不成立回还,所以是不執(zhí)行的,即沒(méi)必要進(jìn)行中斷補(bǔ)償叹洲。
第二種情況柠硕,通過(guò)中斷喚醒,return Thread.interrupted() => true运提,進(jìn)入acquireQueued()的自旋中蝗柔,進(jìn)行再次獲取鎖操作后,此時(shí)interrupted = true民泵,第5步分析的selfInterrupt()的邏輯判斷成立癣丧,進(jìn)行中斷補(bǔ)償。
總結(jié):到此線(xiàn)程的加鎖流程已經(jīng)分析完栈妆,如果競(jìng)爭(zhēng)線(xiàn)程執(zhí)行了最后一步parkAndCheckInterrupt()方法坎缭,則進(jìn)入掛起狀態(tài),等待前面擁有鎖的線(xiàn)程釋放鎖后签钩,競(jìng)爭(zhēng)線(xiàn)程被喚醒掏呼,再次走一遍流程去嘗試獲取鎖,釋放鎖并喚醒等待線(xiàn)程在下一篇文章里再做源碼解析铅檩。
----------------- 文章如有問(wèn)題憎夷,請(qǐng)下方回復(fù)指出,感謝查閱?? -----------------