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,並且鎖需要被釋放兩次才能真正獲得釋放。重入鎖提供了兩種加鎖方式,公平鎖以及非公平鎖。預...