1. 程式人生 > >詳解JUC之鎖——Lock與AQS(02)

詳解JUC之鎖——Lock與AQS(02)

前言

詳解JUC之鎖——概述(01)中我對JUC中的鎖進行了概述,下面我就介紹一下它們的根基Lock介面和AQS

Lock

看名字就知道Lock介面就是JUC中鎖的頂級介面,支援語義不同的鎖規則,比如說公平鎖和非公平鎖,獨佔鎖(也可以叫互斥鎖)和共享鎖等。

它最主要的兩個方法就是lock()unlock(),一看就知道是獲取鎖和釋放鎖。

還有一個比較有趣的方法是boolean tryLock(long time, TimeUnit unit)方法,這個方法沒有lock()方法那麼死心眼,它在嘗試獲取鎖,獲取到了就返回true,一段時間獲取不到就算了,返回一個false

當然了,還有Condition newCondition()

方法,返回與之關聯的Condition物件

AbstractQueuedSynchronizer

AbstractQueuedSynchronizer就是大名鼎鼎的AQS類,怎麼說呢,這個類就是JUC鎖的根基,是JUC中不同鎖的共同抽象類,鎖的許多公共方法都是在這個類中實現的,我給你看看它類繼承結構你就知道了

有沒有發現,無論是帶有Lock字尾的類如ReentrantLock還是不帶Lock字尾的類如Semaphore它們裡面的內部類Sync都繼承了AQS。其實JUC中的各種鎖只是一個表面裝飾,它們裡面真正實現功能的還是Sync

我再來給你看一個比根基更根基的東西,那就是AQS

類的一個成員變數state

/**
 * The synchronization state.
 */
private volatile int state;

看註釋說這個就是同步狀態,可能到現在你還沒有概念。那我問你,鎖最主要的操作是什麼?不就是獲取鎖釋放鎖嘛!!上面我講了JUC中不同鎖的很多公共方法是在AQS類實現的,其中就有獲取鎖和釋放鎖的方法,而我可以大聲地告訴你,JUC中所有鎖的獲取鎖和釋放鎖操作都是圍繞著這個同步狀態state進行加減操作。每次一個執行緒獲取到鎖即對應著這個state加1,釋放鎖就對應著這個state減1(Semaphore就特殊點,它能加n和減n),state為0的時候代表鎖空閒。

公平鎖和非公平鎖

從上面我給出的AQS的繼承結構圖你會發現,很多鎖的名叫Sync的內部類繼承了AQS類,而這個Sync又分別有FairSyncNonfairSync類繼承它,看名字就知道它們分別是公平鎖和非公平鎖了。

那公不公平的準則又是什麼呢?

說到這個得先介紹一下CLH佇列,CLH佇列是AQS中“等待鎖”的執行緒佇列,比如說獨佔鎖被一個執行緒佔用著,其它執行緒就必須等待咯,這些執行緒就是在CLH佇列等待的。這個佇列的是用連結串列實現的,你可以看到AQS類有個內部類Node,這個就是連結串列的節點。CLH是通過自旋+CAS保證節點插入和移除的原子性(這個我在介紹原子類的時候說過類似的),就是說往裡面插入或移除一個節點的時候,在併發條件下不會有問題。

至於CLH是什麼意思,在Node的註釋上有"CLH" (Craig, Landin, and Hagersten) lock queue,目測是三個人的名字縮寫,應該是造出它的人吧。

那現在就可以回答公平的準則了。所謂公平,就是大家排隊,是按照CLH佇列先來先得的規則,即使鎖沒被任何執行緒持有,只要一個執行緒不是處於隊頭,它也會乖乖地等,公平地獲取鎖;非公平,那就是插隊咯,當執行緒要獲取鎖時,它會無視CLH等待佇列而直接獲取鎖,如果鎖沒有被任何執行緒持有,那不管它在CLH的那個角落,它都直接獲取鎖,沒什麼道德可言。

AbstractQueuedLongSynchronizer和AbstractOwnableSynchronizer

至於AbstractQueuedLongSynchronizer類跟AbstractQueuedSynchronizer類差不多,只不過它裡面的statelong型別的,而AbstractQueuedSynchronizer的是int型別的,而且它們都繼承了AbstractOwnableSynchronizer類——一個記錄當前持有獨佔鎖的執行緒的抽象類,這個類很簡單,維護一個Thread型別變數,這個應該就是記錄當前持有獨佔鎖的執行緒,然後提供它的gettersetter方法,它雖然是一個抽象類,但是裡面並沒有抽象方法。