天天看點

深入了解AQS獨占鎖之ReentrantLock源碼分析

作者:悠閑觀自在

1. 管程 — Java同步的設計思想

管程:指的是管理共享變量以及對共享變量的操作過程,讓他們支援并發。

互斥:同一時刻隻允許一個線程通路共享資源;

同步:線程之間如何通信、協作。

MESA模型

在管程的發展史上,先後出現過三種不同的管程模型,分别是Hasen模型、Hoare模型和MESA模型。現在正在廣泛使用的是MESA模型。

深入了解AQS獨占鎖之ReentrantLock源碼分析

管程中引入了條件變量的概念,而且每個條件變量都對應有一個等待隊列。條件變量和等待隊列的作用是解決線程之間的同步問題。

Java中針對管程有兩種實作

  • 一種是基于Object的Monitor機制,用于synchronized内置鎖的實作
  • 一種是抽象隊列同步器AQS,用于JUC包下Lock鎖機制的實作

2. AQS原理分析

2.1 什麼是AQS

java.util.concurrent包中的大多數同步器實作都是圍繞着共同的基礎行為,比如等待隊列、條件隊列、獨占擷取、共享擷取等,而這些行為的抽象就是基于AbstractQueuedSynchronizer(簡稱AQS)實作的,AQS是一個抽象同步架構,可以用來實作一個依賴狀态的同步器。

JDK中提供的大多數的同步器如Lock, Latch, Barrier等,都是基于AQS架構來實作的

  • 一般是通過一個内部類Sync繼承 AQS
  • 将同步器所有調用都映射到Sync對應的方法
深入了解AQS獨占鎖之ReentrantLock源碼分析

AQS具備的特性:

  • 阻塞等待隊列
  • 共享/獨占
  • 公平/非公平
  • 可重入
  • 允許中斷

2.2 AQS核心結構

AQS内部維護屬性volatile int state

  • state表示資源的可用狀态

State三種通路方式:

  • getState()
  • setState()
  • compareAndSetState()

定義了兩種資源通路方式:

  • Exclusive-獨占,隻有一個線程能執行,如ReentrantLock
  • Share-共享,多個線程可以同時執行,如Semaphore/CountDownLatch

AQS實作時主要實作以下幾種方法:

  • isHeldExclusively():該線程是否正在獨占資源。隻有用到condition才需要去實作它。
  • tryAcquire(int):獨占方式。嘗試擷取資源,成功則傳回true,失敗則傳回false。
  • tryRelease(int):獨占方式。嘗試釋放資源,成功則傳回true,失敗則傳回false。
  • tryAcquireShared(int):共享方式。嘗試擷取資源。負數表示失敗;0表示成功,但沒有剩餘可用資源;正數表示成功,且有剩餘資源。
  • tryReleaseShared(int):共享方式。嘗試釋放資源,如果釋放後允許喚醒後續等待結點傳回true,否則傳回false。

2.3 AQS定義兩種隊列

  • 同步等待隊列: 主要用于維護擷取鎖失敗時入隊的線程。
  • 條件等待隊列: 調用await()的時候會釋放鎖,然後線程會加入到條件隊列,調用signal()喚醒的時候會把條件隊列中的線程節點移動到同步隊列中,等待再次獲得鎖。

AQS 定義了5個隊列中節點狀态:

  1. 值為0,初始化狀态,表示目前節點在sync隊列中,等待着擷取鎖。
  2. CANCELLED,值為1,表示目前的線程被取消;
  3. SIGNAL,值為-1,表示目前節點的後繼節點包含的線程需要運作,也就是unpark;
  4. CONDITION,值為-2,表示目前節點在等待condition,也就是在condition隊列中;
  5. PROPAGATE,值為-3,表示目前場景下後續的acquireShared能夠得以執行;

同步等待隊列

AQS當中的同步等待隊列也稱CLH隊列,CLH隊列是Craig、Landin、Hagersten三人發明的一種基于雙向連結清單資料結構的隊列,是FIFO先進先出線程等待隊列,Java中的CLH隊列是原CLH隊列的一個變種,線程由原自旋機制改為阻塞機制。

AQS 依賴CLH同步隊列來完成同步狀态的管理:

  • 目前線程如果擷取同步狀态失敗時,AQS則會将目前線程已經等待狀态等資訊構造成一個節點(Node)并将其加入到CLH同步隊列,同時會阻塞目前線程
  • 當同步狀态釋放時,會把首節點喚醒(公平鎖),使其再次嘗試擷取同步狀态。
  • 通過signal或signalAll将條件隊列中的節點轉移到同步隊列。(由條件隊列轉化為同步隊列)
深入了解AQS獨占鎖之ReentrantLock源碼分析

條件等待隊列

AQS中條件隊列是使用單向清單儲存的,用nextWaiter來連接配接:

  • 調用await方法阻塞線程;
  • 目前線程存在于同步隊列的頭結點,調用await方法進行阻塞(從同步隊列轉化到條件隊列)

3. ReentrantLock源碼分析

ReentrantLock是一種基于AQS架構的應用實作,是JDK中的一種線程并發通路的同步手段,它的功能類似于synchronized是一種互斥鎖,可以保證線程安全。

ReentrantLock基本使用方式

深入了解AQS獨占鎖之ReentrantLock源碼分析

源碼閱讀過程中要關注的問題

1.公平和非公平鎖,可重入鎖是如何實作的

2.設計的精髓:并發場景下入隊和出隊操作是如何設計的

  • 線程競争鎖失敗入隊阻塞邏輯實作
  • 釋放鎖的線程喚醒阻塞線程出隊競争鎖的邏輯實作

結合課上源碼分析流程圖了解ReentrantLock的實作

深入了解AQS獨占鎖之ReentrantLock源碼分析

繼續閱讀