5分鐘 入門AQS

AQS 全稱:AbstractQueuedSynchronizer,它是JUC併發工具包中 ReentrantLock 、CountDownLatch、CyclicBarrier等這些類的底層實現。此篇咱們主要經過ReentrantLock的使用來大致瞭解AQS底層代碼設計原理,不對源碼詳細贅述。只要對總體的設計方向有清晰瞭解,再去追蹤源碼便不是什麼難事。閱讀本篇以前,須要你們對ReentrantLock 的API有必定了解。java

ReentrantLock的lock、await方法底層邏輯

在使用ReentrantLock進行代碼塊同步,通常都會有以下寫法:程序員

ReentrantLock lock = new ReentrantLock(true);
Condition condition = lock.newCondition();

try{
    lock.lock();
    //.....
    condition.await();
    
 }finally{
    lock.unlock();
 }

假設 T1 時刻有10個線程調用同一個ReentrantLock實例的lock()方法, 線程1 先獲取鎖成功,緊接着線程2 調用lock()方法,發現獲取鎖失敗(經過CAS操做對狀態位進行標記),則線程2被封裝成Node節點放入AQS雙向隊列中,並調用LockSupport.park()阻塞掛起。一樣的線程3,線程4,...線程10 也阻塞掛起加入隊列中。至此在t1時刻 AQS的隊列如圖1-1併發

圖1-1

T4 時刻線程1執行完畢,調用unlock()方法釋放鎖並從AQS隊列中取出哨兵節點的下一節點,也就是此刻的線程2,並喚醒該線程。線程2再次嘗試獲取鎖,若是鎖獲取成功將繼續執行線程2代碼。若是失敗會被再次封裝成Node節點放入AQS隊列中去。看到這裏你們可能會有疑問:爲何線程1的鎖釋放完後喚醒的是線程2,而不是線程3 或者線程 4 呢?這裏是由於公平鎖與非公平鎖的緣由,若是採用了公平鎖那麼將按照先進先出的原則讓線程去搶佔鎖,而非公平鎖沒有前後順序的限制。對於ReentrantLock能夠經過構造函數的參數進行指定,默認它採用的是非公平鎖。函數

Lock 與 Condition結合使用又是什麼樣的原理呢?

咱們緊接着上面的分析,線程T1時刻獲取鎖成功後,在T2時刻調用了condition.await()方法時會發生如下4件事情:工具

  1. 釋放鎖(經過CAS操做對狀態爲進行標記)
  2. 阻塞掛起線程1
  3. 線程1封裝成Node節點放入條件隊列中(注意:此處條件隊列和AQS隊列不是一回事)
  4. 喚醒AQS隊列中阻塞掛起的線程

當其餘線程調用condition.signal() 或者 condition.sigalAll() 方法時,會將條件隊列的一個或者所有Node節點移到AQS隊列中。
一個鎖對應一個AQS隊列、多個condition、多個條件隊列,條件隊列的個數是跟隨Condition走的。咱們經過1-2圖來更直觀認識 Lock、Condition、AQS隊列、條件隊列之間的關係。spa

圖1-2

至此ReentrantLock底層使用AQS來實現線程同步的設計已講解完畢,趕忙擼源碼去吧!!!線程


清山綠水始於塵,博學多識貴於勤。
我有酒,你有故事嗎?
公衆號:「Java錦囊」。
歡迎一塊兒談天說地,聊Java。
書寫技術文章是一個按部就班的過程,因此我不能保證每句話、每行代碼都是對的,但至少能保證不復制、不粘貼,每篇文章都是本身對技術的認識、細心斟酌總結出來的。喬布斯說:咱們在這個星球上的時間都很短,不多有機會去作幾件真正偉大的事情,同時要作得好,我必需要趁我還年輕的時候完成這些事。 其實我想說的是,我是一枚程序員,我只想在有限的時間內儘量去沉澱我這一輩子中所能沉澱下來的東西。
相關文章
相關標籤/搜索