AQS
AQS的核心也包括了這些方面:同步隊列,獨佔式鎖的獲取和釋放,共享鎖的獲取和釋放以及可中斷鎖,超時等待鎖獲取這些特性的實現
1.同步隊列
AQS中的同步隊列則是通過鏈式方式進行實現(雙向鏈表沒一個節點都擁有前驅和後繼節點),使用頭尾指針的方式管理同步隊列。
獲取鎖失敗進行入隊操作,獲取鎖成功進行出隊操作。
2.獨佔鎖
(1)獲取獨佔鎖【acquire方法】
調用lock()方法獲取獨佔鎖,獲取失敗就將當前線程加入同步隊列,成功則線程執行,實際上Lock()方法會調用AQS的acquire()方法
A.成功則方法結束返回
B. 失敗則先調用addWaiter() 然後調用acquireQueued()方法進入同步隊列等待
(2)獲取失敗進入等同步隊列
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;
}
當前線程的信息存放到一個新的Node節點,當前鏈表尾節點不爲空直接進行尾插入,compareAndSetTail是一個CAS操作操作失敗會進入自旋進行重試,
如果當前隊列爲null執行enq操作
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;
}
}
}
}
enq操作視爲當前沒有隊列
1.重新穿件一個頭結點 t
2.當t==null執行插入
(3)acquireQueued()方法 排隊獲取鎖
1.獲取當前及誒單的先驅節點
2.判斷當前節點的先驅節點勢頭及誒單並且成功獲取同步狀態,即可以獲取獨佔鎖
3.將隊列的頭指針指向當前節點
4.釋放前驅節點 (前驅節點已經獲取獨佔鎖)
5.獲取失敗 進入等待狀態繼續獲取獨佔鎖
總結
圖片來源:https://juejin.im/post/5aeb07ab6fb9a07ac36350c8
1.線程獲取鎖失敗,線程被封裝成Node進行入隊操作,核心方法在於addWaiter()和enq(),同時enq()完成對同步隊列的頭結點初始化工作以及CAS操作失敗的重試;
2.線程獲取鎖是一個自旋的過程,當且僅當 當前節點的前驅節點是頭結點並且成功獲得同步狀態時,節點出隊即該節點引用的線程獲得鎖,否則,當不滿足條件時就會調用LookSupport.park()方法使得線程阻塞;
3.釋放鎖的時候會喚醒後繼節點;
3.可中斷獲取鎖(acquireInterrutibly)
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
//線程獲取鎖失敗
doAcquireInterruptibly(arg);
}
判斷獲取所失敗則會調用doAcquireInterruptibly
將當前節點同步到隊列中
循環獲取隊列的頭結點出隊,獲取成功將當前頭結點移動下節點
4.超時等待獲取鎖(tryAcquireNanos)
1.在超時時間內,當前線程成功獲取了鎖;
2.當前線程在超時時間內被中斷;
3.超時時間結束,仍未獲得鎖返回false。
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) ||
//實現超時等待的效果
doAcquireNanos(arg, nanosTimeout);
}
doAcquireNanos
1.根據超時時間和當前時間計算出截止時間
2.當前線程獲得鎖出隊列
3.重新計算超時時間
4.線程阻塞等待
5.線程中斷拋出異常
5.共享鎖(acquireShared)
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
tryAcquireShared返回一個int類型大於等於0成功獲取鎖 小於零獲取失敗執行doAcquireShared() 與獨佔鎖類似獲取當前隊列判斷是否爲null將獲取鎖對象的線程變爲Node,尾插入隊列成功進入同步隊列失敗則CAS自旋
進入同步隊列之後若爲投機誒單則獲取同步狀態,不爲頭則插入等待獲取