1. 程式人生 > >多線程系列四:AQS-AbstractQueuedSynchronizer

多線程系列四:AQS-AbstractQueuedSynchronizer

線程阻塞 head 同步隊列 abstract 我們 再次 ive 打包 iter

什麽是AbstractQueuedSynchronizer?為什麽我們要分析它?

AQS:抽象隊列同步器,原理是:當多個線程去獲取鎖的時候,如果獲取鎖失敗了,當前線程就會被打包成一個node節點放入同步隊列裏面使用LockSuportpark方法阻塞起來,如果有線程釋放了鎖,放入同步隊列的線程就會被LockSupportunpark方法喚醒再次去獲取鎖,如果獲取鎖又失敗了就再次打包成node節點放入同步隊列,這樣不斷的循環直到拿到鎖

為什麽藥分析AQS:因為線程都是基於AQS實現的

AQS的基本使用方法

同步器的主要使用方式是繼承,子類通過繼承同步器並實現它的抽象方法來管理同步狀態。可重寫的方法有:

tryAcquire 獨占鎖獲取

tryRelease 獨占鎖釋放

tryAcquireShared 共享鎖獲取

tryReleaseShared 共享鎖釋放

isHeldExclusively 快速判斷被線程獨占

同步器的設計是基於模板方法模式, 使用者需要繼承同步器並重寫指定的方法,隨後將同步器組合在自定義同步組件的實現中,並調用同步器提供的模板方法,而這些模板方法將會調用使用者重寫的方法。

對同步狀態進行更改,這時就需要使用同步器提供的3個方法

getState() 獲取同步狀態

setState 設置同步狀態

compareAndSetState 原子的設置同步狀態來進行操作。

什麽是LockSupport

LockSupport定義了一組的公共靜態方法,這些方法提供了最基本的線程阻塞和喚醒功能,而LockSupport也成為構建同步組件的基礎工具。LockSupport定義了一組以park開頭的方法用來阻塞當前線程,以及unpark(Thread thread)方法來喚醒一個被阻塞的線程。簡單第說:LockSupport就是用來和AQS配合阻塞和喚醒線程的

同步隊列

抽象隊列同步器依賴內部的同步隊列(一個FIFO雙向隊列)來完成同步狀態的管理,當前線程獲取同步狀態失敗時,同步器會將當前線程以及等待狀態等信息構造成為一個節點(Node)並將其加入同步隊列。同步器擁有首節點(head)和尾節點(tail),沒有成功獲取同步狀態的線程將會成為節點加入該隊列的尾部。

鎖的可重入

當一個線程獲得鎖之後,再次進入同步快任然能獲取到鎖執行,實現原理是先判斷當前線程是不是已經獲取到鎖的線程,如果是就把狀態加1;釋放鎖的時候拿了幾次鎖就要釋放幾次

如果沒有實現鎖的可重入則會出現一個線程拿到了鎖之後再次獲取鎖,這樣就會自己等待自己出現死鎖

公平鎖和非公平鎖

公平鎖:一個線程去競爭鎖的時候先看前面有沒有其他線程在等待獲取鎖,如果有就不參與

非公平鎖:一個線程去獲取鎖的時候不管前面有沒有線程在等待獲取鎖都有參與競爭

ReentrantReadWriteLock的實現原理

讀寫鎖的自定義同步器需要在同步狀態(一個整型變量)上維護多個讀線程和一個寫線程的狀態,使得該狀態的設計成為讀寫鎖實現的關鍵。如果在一個整型變量上維護多種狀態,就一定需要“按位切割使用”這個變量,讀寫鎖將變量切分成了兩個部分,高16位表示讀,低16位表示寫。

讀狀態是所有線程獲取讀鎖次數的總和,而每個線程各自獲取讀鎖的次數只能選擇保存在ThreadLocal中,由線程自身維護。

Condition的實現原理

condition是等待通知機制的另一種方式

等待隊列是一個FIFO的隊列,在隊列中的每個節點都包含了一個線程引用,該線程就是在Condition對象上等待的線程,如果一個線程調用了Condition.await()方法,那麽該線程將會釋放鎖、構造成節點加入等待隊列並進入等待狀態。

一個Condition包含一個等待隊列,新增節點只需要將原有的尾節點nextWaiter指向它,並且更新尾節點即可。

調用Condition的signal()方法,將會喚醒在等待隊列中等待時間最長的節點(首節點),在喚醒節點之前,會將節點移到同步隊列中。

調用該方法的前置條件是當前線程必須獲取了鎖,可以看到signal()方法進行了isHeldExclusively()檢查,也就是當前線程必須是獲取了鎖的線程。接著獲取等待隊列的首節點,將其移動到同步隊列並使用LockSupport喚醒節點中的線程。

通過調用同步器的enq(Node node)方法,等待隊列中的頭節點線程安全地移動到同步隊列。

Condition的signalAll()方法,相當於對等待隊列中的每個節點均執行一次signal()方法,效果就是將等待隊列中所有節點全部移動到同步隊列中,並喚醒每個節點的線程。

多線程系列四:AQS-AbstractQueuedSynchronizer