共享鎖的實(shí)現(xiàn)
共享鎖允許多個線程持有。
1.acquireShared()
//acquireShared()
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
//doAcquireShared()
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
// 一旦共享獲取成功燎孟,設(shè)置新的頭結(jié)點(diǎn)怨规,并且喚醒后繼線程
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
2.setHeadAndPropagate(Node node,int propagate)
/**
* 這個函數(shù)做的事情有兩件:
* 1. 在獲取共享鎖成功后疙驾,設(shè)置head節(jié)點(diǎn)
* 2. 根據(jù)調(diào)用tryAcquireShared返回的狀態(tài)以及節(jié)點(diǎn)本身的等待狀態(tài)來判斷是否要需要喚醒后繼線程启上。
*/
private void setHeadAndPropagate(Node node, int propagate) {
// 把當(dāng)前的head封閉在方法棧上,用以下面的條件檢查媒吗。
Node h = head;
setHead(node);
/*
* propagate是tryAcquireShared的返回值辈毯,這是決定是否傳播喚醒的依據(jù)之一坝疼。
* h.waitStatus為SIGNAL或者PROPAGATE時也根據(jù)node的下一個節(jié)點(diǎn)共享來決定是否傳播喚醒,
* 這里為什么不能只用propagate > 0來決定是否可以傳播在本文下面的思考問題中有相關(guān)講述漓摩。
*/
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}
3.deReleaseShared()
/**
* 這是共享鎖中的核心喚醒函數(shù)裙士,主要做的事情就是喚醒下一個線程或者設(shè)置傳播狀態(tài)。
* 后繼線程被喚醒后管毙,會嘗試獲取共享鎖腿椎,如果成功之后桌硫,則又會調(diào)用setHeadAndPropagate,將喚醒傳播下去。
* 這個函數(shù)的作用是保障在acquire和release存在競爭的情況下啃炸,保證隊列中處于等待狀態(tài)的節(jié)點(diǎn)能夠有辦法被喚醒铆隘。
*/
private void doReleaseShared() {
/*
* 以下的循環(huán)做的事情就是,在隊列存在后繼線程的情況下南用,喚醒后繼線程膀钠;
* 或者由于多線程同時釋放共享鎖由于處在中間過程,讀到head節(jié)點(diǎn)等待狀態(tài)為0的情況下裹虫,
* 雖然不能unparkSuccessor肿嘲,但為了保證喚醒能夠正確穩(wěn)固傳遞下去,設(shè)置節(jié)點(diǎn)狀態(tài)為PROPAGATE筑公。
* 這樣的話獲取鎖的線程在執(zhí)行setHeadAndPropagate時可以讀到PROPAGATE雳窟,從而由獲取鎖的線程去釋放后繼等待線程。
*/
for (;;) {
Node h = head;
// 如果隊列中存在后繼線程匣屡。
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
unparkSuccessor(h);
}
// 如果h節(jié)點(diǎn)的狀態(tài)為0封救,需要設(shè)置為PROPAGATE用以保證喚醒的傳播。
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue;
}
// 檢查h是否仍然是head捣作,如果不是的話需要再進(jìn)行循環(huán)誉结。
if (h == head)
break;
}
}
4.releaseShared()釋放共享鎖
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
// doReleaseShared的實(shí)現(xiàn)上面獲取共享鎖已經(jīng)介紹
doReleaseShared();
return true;
}
return false;
}
參考:
https://blog.csdn.net/luofenghan/article/details/75065001
http://www.cnblogs.com/micrari/p/6937995.html
https://liuzhengyang.github.io/2017/05/12/aqs/