AQS原理和用法:http://ifeve.com/introduce-abstractqueuedsynchronizer/
AQS源碼分析:http://cmsblogs.com/?p=2205
AQS源碼分析:https://zhuanlan.zhihu.com/p/38010971
ConditionObject源碼:http://www.reibang.com/p/4d4c7398e187
CLH鎖的基本原理
使用FIFO隊(duì)列保證公平性
有當(dāng)前節(jié)點(diǎn)和前置節(jié)點(diǎn)赁濒,當(dāng)前節(jié)點(diǎn)不斷自旋鹦付,查詢(監(jiān)聽)前置節(jié)點(diǎn)的狀態(tài)(isLocked)(保證了FIFO)
一系列的前置節(jié)點(diǎn)和當(dāng)前節(jié)點(diǎn)構(gòu)成隊(duì)列
當(dāng)前節(jié)點(diǎn)運(yùn)行完成后,更改自己的狀態(tài)试伙,那監(jiān)聽當(dāng)前節(jié)點(diǎn)狀態(tài)的線程就會(huì)結(jié)束自旋
節(jié)點(diǎn)狀態(tài)
1、CANCELLED睹逃,值為1 牙咏。場(chǎng)景:當(dāng)該線程等待超時(shí)或者被中斷,需要從同步隊(duì)列中取消等待做院,則該線程被置1盲泛,即被取消(這里該線程在取消之前是等待狀態(tài))。節(jié)點(diǎn)進(jìn)入了取消狀態(tài)則不再變化键耕;
2寺滚、SIGNAL,值為-1屈雄。當(dāng)前節(jié)點(diǎn)正在等待一個(gè)release的信號(hào)村视;
3、CONDITION酒奶,值為-2蚁孔。在等待隊(duì)列中值為-2,當(dāng)轉(zhuǎn)移到同步隊(duì)列時(shí)候惋嚎,-2改為0杠氢;
4、PROPAGATE另伍,值為-3鼻百。場(chǎng)景:表示下一次的共享狀態(tài)會(huì)被無條件的傳播下去;
5、INITIAL温艇,值為0因悲,初始狀態(tài)。
共享鎖代碼分析
共享鎖獲取
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
1中贝、如果獲取資源成功囤捻,則不阻塞,代碼繼續(xù)往后走邻寿。
2蝎土、doAcquireShared方法詳細(xì)見下
3、tryAcquireShared是非阻塞的绣否。<0 獲取資源失斕苎摹;0:獲取資源成功蒜撮,后續(xù)節(jié)點(diǎn)不可能成功暴构;>1:獲取資源成功,且后續(xù)節(jié)點(diǎn)獲取資源也可能成功段磨。
4取逾、共享鎖允許多個(gè)線程持有同一個(gè)鎖,所以喚醒時(shí)候需要進(jìn)行傳播苹支,以便喚醒全部等待線程砾隅。
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED); //把節(jié)點(diǎn)加入同步隊(duì)列的隊(duì)尾
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) { // 自旋退出條件:前驅(qū)節(jié)點(diǎn)是頭節(jié)點(diǎn),并且try成功债蜜。本線程由前驅(qū)節(jié)點(diǎn)線程進(jìn)行unpark
setHeadAndPropagate(node, r);//刪除前驅(qū)節(jié)點(diǎn)晴埂,把當(dāng)前節(jié)點(diǎn)設(shè)置為head,如果滿足propagate寻定,則調(diào)用doReleaseShared
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL) //前驅(qū)節(jié)點(diǎn)正在等待信號(hào)儒洛,所以當(dāng)前節(jié)點(diǎn)必然也要等待
return true;
if (ws > 0) { //前驅(qū)節(jié)點(diǎn)已經(jīng)被取消
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;//刪除所有已取消的節(jié)點(diǎn),并繼續(xù)自旋
} else { //ws=0,-2,-3
compareAndSetWaitStatus(pred, ws, Node.SIGNAL); //設(shè)置前驅(qū)節(jié)點(diǎn)為signal狼速,并繼續(xù)自旋
}
return false;
}
共享鎖的釋放
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) { //如果隊(duì)列為空則直接退出
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) //重置頭結(jié)點(diǎn)狀態(tài)為0
continue;
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // 頭節(jié)點(diǎn)變化琅锻,說明有線程已經(jīng)喚醒,可以退出循環(huán)
break;
}
}
private void unparkSuccessor(Node node) { //傳入的是頭節(jié)點(diǎn)
int ws = node.waitStatus;
if (ws < 0) //再次設(shè)置確保頭結(jié)點(diǎn)為0
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next; //獲取待釋放的節(jié)點(diǎn)
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev) //參考https://www.zhihu.com/question/50724462
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
為什么unparkSuccessor在后續(xù)節(jié)點(diǎn)斷裂向胡,從tail開始查找浅浮,原因在于enq方法插入時(shí):新節(jié)點(diǎn)pre指向tail,tail指向新節(jié)點(diǎn)捷枯,這里后繼指向前驅(qū)的指針是由CAS操作保證線程安全的滚秩。而cas操作之后t.next=node之前,可能會(huì)有其他線程進(jìn)來淮捆。所以出現(xiàn)了問題郁油,從尾部向前遍歷是一定能遍歷到所有的節(jié)點(diǎn)本股。
獨(dú)占鎖分析
獨(dú)占鎖獲取
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
1、acquireQueued類似doAcquireShared
2桐腌、
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
獨(dú)占鎖釋放
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); //獨(dú)占鎖不需要考慮共享鎖的級(jí)聯(lián)解鎖問題
return true;
}
return false;
}
條件變量CondtionObject
await和signal配合操作原理
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
1拄显、構(gòu)造等待節(jié)點(diǎn),并加入等待隊(duì)列案站;
2躬审、釋放當(dāng)前持有的排他鎖;
3蟆盐、如果不在同步隊(duì)列中承边,則進(jìn)行park,掛起線程石挂;
4博助、當(dāng)調(diào)用signal后,會(huì)把節(jié)點(diǎn)加入同步隊(duì)列痹愚,并喚醒a(bǔ)wait中的的線程富岳;
5、await在park后繼續(xù)運(yùn)行拯腮,退出自旋窖式,并嘗試強(qiáng)鎖;
6动壤、得到鎖后運(yùn)行后續(xù)程序萝喘。
final boolean transferForSignal(Node node) {
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}