java_Lock【AQS】

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自旋
進入同步隊列之後若爲投機誒單則獲取同步狀態,不爲頭則插入等待獲取

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章