當前位置:網站首頁>通過ReentrantLock源碼看AQS源碼實現

通過ReentrantLock源碼看AQS源碼實現

2022-01-27 18:46:46 娃哈哈哈哈哈哈哈哈哈哈哈哈哈哈

ReentrantLock是基於AQS實現的,通過ReentrantLock的公平鎖研究一下AQS線程競爭做出的操作
其中AQS比較重要的幾個屬性先介紹一下:
**
private transient volatile Node head; 隊列頭
private transient volatile Node tail; 隊列尾
private volatile int state; 鎖狀態**
Node node 這個節點包含了三個屬性
volatile Node prev; 上一個節點
volatile Node next; 下一個節點
volatile Thread thread; 線程

可以先把這個node理解為一個鏈錶的數據結構
假如現在是t1線程進來
首先先調用公平鎖的lock方法,加鎖

在這裏插入圖片描述
執行AQS封裝的方法,
在這裏插入圖片描述
然後查看ReentrantLock公平鎖自己的實現
先執行第一個tryAcquire方法,這個方法會去反,先記一下
先拿到當前線程,當前線程為t1,獲取狀態,狀態默認為0,沒有被修改過,然後==0,進去執行hasQueuedPredecessprs方法 這裏也是去反

在這裏插入圖片描述
將隊列尾tail屬性給到t,頭head屬性給到h,然後定義一個s,這裏隊列還沒有被初始化,只是做了一個賦值操作 然後判斷h!=t 這裏這兩個屬性都是默認為null的屬性,所以null!=null返回false,然後短路跳出返回false
在這裏插入圖片描述
去反得到true,開始執行compareAndSetState方法,cas設置狀態,調用lock時傳入的值為1,期望值為0,修改state即為搶占鎖
在這裏插入圖片描述
下面這個方法就是把當前線程給到一個屬性,然後執行完畢
在這裏插入圖片描述
返回true,證明拿到鎖
在這裏插入圖片描述
如果是第二次再進來加鎖,就會走下邊的方法,因為他第一次進來已經吧狀態改為了1,下面就是看ReentrantLock的可重入鎖判斷,判斷當前線程是不是持有鎖的線程,如果是就狀態+1,錶示重入了鎖一次

然後假如t1開始執行任務,然後t2進來開始競爭鎖
t2也調用lock方法

在這裏插入圖片描述

上面這個tryAcquire方法t2走了以後就會返回false,然後去反,開始執行acquireQueued(addWaiter(Node.EXCLUSIVE),arg),先看addWaiter方法
首先是創建一個當前線程的node節點,然後創建一個新的node節點然後把尾屬性tail給它,現在pred是一個null,因為tail是null,然後判斷null!=null,返回false,執行enq方法
在這裏插入圖片描述
一個死循環,創建一個新的node節點,tail還是null,所以t也是null,然後cas設置頭,給head創建了一個新的node,然後把head頭給到tail,現在這這裏隊列算是初始化了,頭和尾都指向同一個node,但這個node屬性是沒有值的,然後再次循環,這次tail不等於null,然後讓這個node節點指向t這個節點,然後cas設置t的尾,尾就是當前線程的node,現在隊列裏空的node節點在第一個,next指向t2的node,然後t2的prev執行第一個節點,把第一個node節點返回

在這裏插入圖片描述
執行完這個將這個當前線程的node返回出去
在這裏插入圖片描述
然後開始執行這個方法,進來以後定義兩個屬性開始死循環,然後取出node的perv,這裏是當前線程的node,所以他的perv是第一個node,然後第一個node是==head的,然後自旋一次獲取鎖,如果獲取所成功,就把頭設置為當前線程的node,然後把第一個的next節點指向null,這樣沒有了飲用第一個node就可以被gc回收,然後獲取鎖成功,這裏我們假設沒有成功,執行下邊的shouldParkAfterFailedAcquire
在這裏插入圖片描述
第一個參數node是隊列第一個node,第二個node是當前線程的node,然後拿出前一個node的ws狀態,
ws為0,signal為-1,判斷小於0,不小於,然後cas設置這個狀態為-1,這個-1會錶示前面那個線程進入了睡眠,後面會在解鎖時候用到,然後返回false,循環以後會再一次自旋去拿鎖,如果再次拿鎖失敗,那麼再進入當前方法,這次就等於-1了,返回true,然後會執行parkAndCheckInterrupt方法
在這裏插入圖片描述

在這裏插入圖片描述
就會執行這個方法,調用park方法,讓線程進入睡眠
這裏解釋一下為什麼前面要調用兩三次自旋去拿鎖,因為如果調用了park方法,那麼就會讓os參與,也就是說,變成了一把重量級鎖,所以會想讓他自旋拿鎖,如果拿到鎖,不調用park方法,那麼他就是一個輕量級鎖,效率肯定是會高於重量級鎖的,然後睡眠以後就看解鎖
在這裏插入圖片描述
在這裏插入圖片描述
先調用tryRelease方法
在這裏插入圖片描述
這裏拿到鎖的狀態,鎖為1錶示加1次鎖,為2錶示重入了一次.一次類推,這裏是t1只進入了一次,就是1,1-1所以c=0,然後設置當前鎖線程為null,然後設置鎖狀態為0,返回free為true,然後把第一個node給h,這裏是判斷是否有線程在隊列,然後在判斷狀態是否為0,如果不為0錶示有線程在等待,就執行unpark方法,喚醒在等待的第一個線程繼續執行,完成t1的解鎖

在這裏插入圖片描述

版權聲明
本文為[娃哈哈哈哈哈哈哈哈哈哈哈哈哈哈]所創,轉載請帶上原文鏈接,感謝
https://cht.chowdera.com/2022/01/202201271846461625.html

隨機推薦