AQS和ReentrantLock
ReentrantLock的lock特點
-
異常的鎖是包含死鎖和活鎖,死鎖無法解決,活鎖可以通過錯開執行時間解決
-
lock的特點:可打斷,可重入,可設置超時時間,可設置鎖類型(公平非公平),支持多變量,支持鎖類型(讀,寫)
-
以上都是基礎概念,例子就不寫了,至於多變量condition,這個我也沒用過,大概意思,用對應的condition去喚醒,下面是例子
AQS框架的樣子
-
AbstractQueuedSychronizer 同步隊列,他有一個屬性state表示鎖的狀態,數字表示獲得鎖的次數,利用cas改變其數值;其中有倆吧鎖,獨佔鎖,共享鎖(相鄰的讀讀共享,讀寫互斥), 它的鎖都支持公平和非公平兩種模式 ;內部有一個隊列,fifo,格式大概是,有一個head指向隊首,一個tail指向隊尾,具體結構如下圖;條件變量實現等待喚醒機制(我也不太懂,沒用過);內部有一個thread exclusiveOwnerThread記錄持有鎖的線程;
-
其主要是實現的功能:(這個就是一些概念,知道就行)
-
功能
1、實現阻塞獲取鎖 acquire 拿不到鎖就去阻塞 等待鎖被釋放再次獲取鎖
2、實現非阻塞嘗試獲取鎖 tryAcquire 拿不到鎖則直接放棄
3、實現獲取鎖超時機制
4、實現通過打斷來取消
5、實現獨佔鎖及共享鎖
6、實現條件不滿足的時候等待
-
-
自定義實現AQS框架:如下圖感覺沒啥意思,也沒有可重入的實現
ReentrantLock的加鎖和解鎖流程
-
首先看他和AQS關係:關係如下,圖中包含公平鎖,非公平鎖
-
看nonfairSync的加鎖過程:以下面的例子爲例,大概情況爲(//這裏少了排隊中隊列元素的屬性,Node.waitStatus,)
- 首先t1進來加鎖,此時無競爭
- ti拿到了鎖t2來了
- t1釋放了鎖,t2開始拿鎖
- t1拿到了鎖,t2t3在排隊
-
線程打斷:線程存在一箇中斷標記,這個標記的含義是記錄自己是否被別的線程中斷過,清除既標記製爲false;那如何看這個標記呢t1.interrupted(),具體代碼如下
這段英文是介紹說這個方法被其他線程中斷時會調用這個方法
這段介紹的是當前線程以獨佔鎖形式被中斷,如果中斷則終止,否則繼續嘗試拿鎖,失敗排隊
拿鎖失敗後的過程,排隊拿鎖
-
看解鎖流程,先看幾張圖,大致流程如下ti持有鎖,t2排隊,t1釋放鎖,t2持有鎖,這裏還有一個Node。waitStatus的狀態。當狀態爲-1,作用是隊首節點拿到鎖後是否喚醒下一個節點
-
讀讀併發需注意的問題:這個是別人寫的例子,證明讀讀併發,讀寫互斥
-
讀寫鎖之寫鎖上鎖過程:由於讀寫互斥,多以寫鎖加鎖時要麼鎖沒有別人持有,或者鎖重入纔可以加上鎖;關於鎖的數據結構,比如32bit表示鎖,前16位爲讀鎖,後16位爲寫鎖;
具體流程如下:reentrantLock的writeLock
這裏直接從tryAcqurie方法開始,首先獲取當前線程,或許鎖的狀態state,獲取寫鎖的狀態(後16位),如果state不等於0,要判斷當前是不是寫鎖,如果w==0則代表沒上過寫鎖,只是讀鎖,直接返回false,如果是寫鎖則判斷佔有這把鎖的是不是當前線程,如果託不是則返回false;如果這個判斷過了則判斷重入次數是否超過最大限制,如果沒有就設置state,返回true;如果state=0則是第一次來,那就要區分是否是公平鎖還是非公平鎖,如果非公平則直接拿鎖,公平則排隊,如果隊裏沒人則直接拿鎖,如果隊裏有人則加鎖失敗返回false -
讀寫鎖之讀鎖上鎖過程
具體流程如下:reentrantLock之讀鎖上鎖
首先獲取當前線程,當前鎖狀態state,如果exclusiveCount(C)!=0代表是寫鎖,並且獨佔鎖還不是當前線程,則返回-1;如果上訴情況沒有發生則看上鎖次數,如果上鎖次數小於最大值,並且不排隊,並且cas(c,c+share_unit)如果都滿足則進入讀鎖加鎖判定中,在這裏如果讀鎖r=0,則代表第一個線程第一次加鎖,如果讀鎖r!=0,但是第一個讀鎖線程是當前線程,或者相鄰的讀鎖進入,都返回1 -
關於搶鎖的時候非公平搶了幾次鎖
第一次:使用無阻塞的cas去搶鎖,第二次,如對之前如何上一個節點是head則再搶一次,搶不到就去排隊