ReentrantLock之AQS原理與原始碼詳解

2021-09-13 02:11:43 字數 2004 閱讀 8234

abstractqueuedsynchronizer,抽象佇列同步器;給大家畫乙個圖先,看一下reentrantlock和aqs之間的關係。

abstractqueuedsynchronizer為reentrantlock的靜態內部類

2、預設為非公平鎖

3、最終會呼叫abstractqueuedsynchronizer子類nonfairsync.lock()方法;

lock()方法做了什麼事呢?

首先需要知道aqs會維護兩個變數state(初始值0)、exclusiveownerthread(初始值null),原始碼如下,記錄執行緒狀態與當前加鎖執行緒

執行緒1跑過來呼叫reentrantlock的lock()方法嘗試進行加鎖,這個加鎖的過程,直接就是用cas操作將state值從0變為1。

如果之前沒人加過鎖,那麼state的值肯定是0,此時執行緒1就可以加鎖成功。

一旦執行緒1加鎖成功了之後,就可以設定當前加鎖執行緒是自己。所以大家看下面的圖,就是執行緒1跑過來加鎖的乙個過程。

state記錄加鎖次數,為0時釋放鎖

執行緒2跑過來一下看到,哎呀!state的值不是0啊?所以cas操作將state從0變為1的過程會失敗,因為state的值當前為1,說明已經有人加鎖了!

接著執行緒2會看一下,是不是自己之前加的鎖啊?當然不是了,「加鎖執行緒」這個變數明確記錄了是執行緒1占用了這個鎖,所以執行緒2此時就是加鎖失敗。

接著,執行緒2會將自己放入aqs中的乙個等待佇列,因為自己嘗試加鎖失敗了,此時就要將自己放入佇列中來等待,等待執行緒1釋放鎖之後,自己就可以重新嘗試加鎖了

所以大家可以看到,aqs是如此的核心!aqs內部還有乙個等待佇列,專門放那些加鎖失敗的執行緒!

同樣,給大家來一張圖,一起感受一下:

原始碼

執行緒2進來加鎖失敗後,會進入等待佇列;等待隊列為鍊錶

接著,執行緒1在執行完自己的業務邏輯**之後,就會釋放鎖!他釋放鎖的過程非常的簡單,就是將aqs內的state變數的值遞減1,如果state值為0,則徹底釋放鎖,會將「加鎖執行緒」變數也設定為null!

整個過程,參見下圖:

locksupport.unpark(s.thread);

接下來,會從等待佇列的隊頭喚醒執行緒2重新嘗試加鎖。

好!執行緒2現在就重新嘗試加鎖,這時還是用cas操作將state從0變為1,此時就會成功,成功之後代表加鎖成功,就會將state設定為1。

此外,還要把「加鎖執行緒」設定為執行緒2自己,同時執行緒2自己就從等待佇列**隊了。

最後再來一張圖,大家來看看這個過程。

參考資料

ReentrantLock之unlock方法分析

public void unlock public final boolean release int arg return false release 1 嘗試在當前鎖的鎖定計數 state 值上減1。成功返回true,否則返回false。當然在release 方法中不僅僅只是將state 1這麼...

併發學習之 ReentrantLock

在 jdk 6 之前,儘管 synchronized 關鍵字實現同步很方便,但是這種同步操作很重量級,很大程度上影響程式的執行效率,所以對於開發者來說,使用起來會有點畏懼。但是在 jdk 6 之後,對 synchronized 進行了很大的優化,引入了偏向鎖,適應性自旋,輕量級鎖和重量級鎖,鎖粗化等...

JUC之重入鎖ReentrantLock

重入鎖是一種遞迴無堵塞的同步機制。類似於synchronized,但是比synchronized更加的靈活,可自由選擇加鎖的位置。它有乙個與鎖相關的獲取計數器,如果擁有鎖的某個執行緒再次得到鎖,那麼獲取計數器加1,並且鎖需要被釋放兩次才能真正獲得釋放。重入鎖提供了兩種加鎖方式,公平鎖以及非公平鎖。預...