魔鬼在細節,理解Java並發底層之AQS實現

  • 2019 年 10 月 9 日
  • 筆記

jdk的JUC包(java.util.concurrent)提供大量Java並發工具提供使用,基本由Doug Lea編寫,很多地方值得學習和借鑒,是進階升級必經之路

本文從JUC包中常用的對象鎖、並發工具的使用和功能特性入手,帶著問題,由淺到深,一步步剖析並發底層AQS抽象類具體實現

名詞解釋

1 AQS

AQS是一個抽象類,類全路徑java.util.concurrent.locks.AbstractQueuedSynchronizer,抽象隊列同步器,是基於模板模式開發的並發工具抽象類,有如下並發類基於AQS實現:

2 CAS

CAS是Conmpare And Swap(比較和交換)的縮寫,是一個原子操作指令

CAS機制當中使用了3個基本操作數:記憶體地址addr,預期舊的值oldVal,要修改的新值newVal
更新一個變數的時候,只有當變數的預期值oldVal和記憶體地址addr當中的實際值相同時,才會將記憶體地址addr對應的值修改為newVal

基於樂觀鎖的思路,通過CAS再不斷嘗試和比較,可以對變數值執行緒安全地更新

3 執行緒中斷

執行緒中斷是一種執行緒協作機制,用於協作其他執行緒中斷任務的執行

當執行緒處於阻塞等待狀態,例如調用了wait()、join()、sleep()方法之後,調用執行緒的interrupt()方法之後,執行緒會馬上退出阻塞並收到InterruptedException;

當執行緒處於運行狀態,調用執行緒的interrupt()方法之後,執行緒並不會馬上中斷執行,需要在執行緒的具體任務執行邏輯中通過調用isInterrupted() 方法檢測執行緒中斷標誌位,然後主動響應中斷,通常是拋出InterruptedException

對象鎖特性

下面先介紹對象鎖、並發工具有哪些基本特性,後面再逐步展開這些特性如何實現

1 顯式獲取

以ReentrantLock鎖為例,主要支援以下4種方式顯式獲取鎖

  • (1) 阻塞等待獲取
ReentrantLock lock = new ReentrantLock();  // 一直阻塞等待,直到獲取成功  lock.lock();
  • (2) 無阻塞嘗試獲取
ReentrantLock lock = new ReentrantLock();  // 嘗試獲取鎖,如果鎖已被其他執行緒佔用,則不阻塞等待直接返回false  // 返回true - 鎖是空閑的且被本執行緒獲取,或者已經被本執行緒持有  // 返回false - 獲取鎖失敗  boolean isGetLock = lock.tryLock();
  • (3) 指定時間內阻塞等待獲取
ReentrantLock lock = new ReentrantLock();  try {      // 嘗試在指定時間內獲取鎖      // 返回true - 鎖是空閑的且被本執行緒獲取,或者已經被本執行緒持有      // 返回false - 指定時間內未獲取到鎖      lock.tryLock(10, TimeUnit.SECONDS);  } catch (InterruptedException e) {      // 內部調用isInterrupted() 方法檢測執行緒中斷標誌位,主動響應中斷      e.printStackTrace();  }
  • (4) 響應中斷獲取
ReentrantLock lock = new ReentrantLock();  try {      // 響應中斷獲取鎖      // 如果調用執行緒的thread.interrupt()方法設置執行緒中斷,執行緒退出阻塞等待並拋出中斷異常      lock.lockInterruptibly();  } catch (InterruptedException e) {      e.printStackTrace();  }

2 顯式釋放

ReentrantLock lock = new ReentrantLock();  lock.lock();  // ... 各種業務操作  // 顯式釋放鎖  lock.unlock();

3 可重入

已經獲取到鎖的執行緒,再次請求該鎖可以直接獲得

4 可共享

指同一個資源允許多個執行緒共享,例如讀寫鎖的讀鎖允許多個執行緒共享,共享鎖可以讓多個執行緒並發安全地訪問數據,提高程式執效率

5 公平、非公平

公平鎖:多個執行緒採用先到先得的公平方式競爭鎖。每次加鎖前都會檢查等待隊列裡面有沒有執行緒排隊,沒有才會嘗試獲取鎖。
非公平鎖:當一個執行緒採用非公平的方式獲取鎖時,該執行緒會首先去嘗試獲取鎖而不是等待。如果沒有獲取成功,才會進入等待隊列

因為非公平鎖方式可以使後來的執行緒有一定幾率直接獲取鎖,減少了執行緒掛起等待的幾率,性能優於公平鎖

AQS實現原理

1 基本概念

(1) Condition介面

類似Object的wait()、wait(long timeout)、notify()以及notifyAll()的方法結合synchronized內置鎖可以實現可以實現等待/通知模式,實現Lock介面的ReentrantLock、ReentrantReadWriteLock等對象鎖也有類似功能:

Condition介面定義了await()、awaitNanos(long)、signal()、signalAll()等方法,配合對象鎖實例實現等待/通知功能,原理是基於AQS內部類ConditionObject實現Condition介面,執行緒await後阻塞並進入CLH隊列(下面提到),等待其他執行緒調用signal方法後被喚醒

(2) CLH隊列

CLH隊列,CLH是演算法提出者Craig, Landin, Hagersten的名字簡稱

AQS內部維護著一個雙向FIFO的CLH隊列,AQS依賴它來管理等待中的執行緒,如果執行緒獲取同步競爭資源失敗時,會將執行緒阻塞,並加入到CLH同步隊列;當競爭資源空閑時,基於CLH隊列阻塞執行緒並分配資源

CLH的head節點保存當前佔用資源的執行緒,或者是沒有執行緒資訊,其他節點保存排隊執行緒資訊

CLH

CLH中每一個節點的狀態(waitStatus)取值如下:

  • CANCELLED(1):表示當前節點已取消調度。當timeout或被中斷(響應中斷的情況下),會觸發變更為此狀態,進入該狀態後的節點將不會再變化
  • SIGNAL(-1):表示後繼節點在等待當前節點喚醒。後繼節點入隊後進入休眠狀態之前,會將前驅節點的狀態更新為SIGNAL
  • CONDITION(-2):表示節點等待在Condition上,當其他執行緒調用了Condition的signal()方法後,CONDITION狀態的節點將從等待隊列轉移到同步隊列中,等待獲取同步鎖
  • PROPAGATE(-3):共享模式下,前驅節點不僅會喚醒其後繼節點,同時也可能會喚醒後繼的後繼節點
  • 0:新節點入隊時的默認狀態

(3) 資源共享方式

AQS定義兩種資源共享方式:
Exclusive 獨佔,只有一個執行緒能執行,如ReentrantLock
Share 共享,多個執行緒可同時執行,如Semaphore/CountDownLatch

(4) 阻塞/喚醒執行緒的方式

AQS 基於sun.misc.Unsafe類提供的park方法阻塞執行緒,unpark方法喚醒執行緒,被park方法阻塞的執行緒能響應interrupt()中斷請求退出阻塞

2 基本設計

核心設計思路:AQS提供一個框架,用於實現依賴於CLH隊列的阻塞鎖和相關的並發同步器。子類通過實現判定是否能獲取/釋放資源的protect方法,AQS基於這些protect方法實現對執行緒的排隊、喚醒的執行緒調度策略

AQS還提供一個支援執行緒安全原子更新的int類型變數作為同步狀態值(state),子類可以根據實際需求,靈活定義該變數代表的意義進行更新

通過子類重新定義的系列protect方法如下:

  • boolean tryAcquire(int) 獨佔方式嘗試獲取資源,成功則返回true,失敗則返回false
  • boolean tryRelease(int) 獨佔方式嘗試釋放資源,成功則返回true,失敗則返回false
  • int tryAcquireShared(int) 共享方式嘗試獲取資源。負數表示失敗;0表示成功,但沒有剩餘可用資源;正數表示成功,且有剩餘資源
  • boolean tryReleaseShared(int) 共享方式嘗試釋放資源,如果釋放後允許喚醒後續等待節點返回true,否則返回false

這些方法始終由需要需要調度協作的執行緒來調用,子類須以非阻塞的方式重新定義這些方法

AQS基於上述tryXXX方法,對外提供下列方法來獲取/釋放資源:

  • void acquire(int) 獨佔方式獲取到資源,執行緒直接返回,否則進入等待隊列,直到獲取到資源為止,且整個過程忽略中斷的影響
  • boolean release(int) 獨佔方式下執行緒釋放資源,先釋放指定量的資源,如果徹底釋放了(即state=0),它會喚醒等待隊列里的其他執行緒來獲取資源
  • void acquireShared(int) 獨佔方式獲取資源
  • boolean releaseShared(int) 共享方式釋放資源

以獨佔模式為例:獲取/釋放資源的核心的實現如下:

 Acquire:       while (!tryAcquire(arg)) {          如果執行緒尚未排隊,則將其加入隊列;       }     Release:       if (tryRelease(arg))          喚醒CLH中第一個排隊執行緒

到這裡,有點繞,下面一張圖把上面介紹到的設計思路再重新捋一捋:

AQS基本设计

特性實現

下面介紹基於AQS的對象鎖、並發工具的一系列功能特性的實現原理

1 顯式獲取

該特性還是以ReentrantLock鎖為例,ReentrantLock是可重入對象鎖,執行緒每次請求獲取成功一次鎖,同步狀態值state加1,釋放鎖state減1,state為0代表沒有任何執行緒持有鎖

ReentrantLock鎖支援公平/非公平特性,下面的顯式獲取特性以公平鎖為例

(1) 阻塞等待獲取

基本實現如下:

  • 1、ReentrantLock實現AQS的tryAcquire(int)方法,先判斷:如果沒有任何執行緒持有鎖,或者當前執行緒已經持有鎖,則返回true,否則返回false
  • 2、AQS的acquire(int)方法判斷當前節點是否為head且基於tryAcquire(int)能否獲得資源,如果不能獲得,則加入CLH隊列排隊阻塞等待
  • 3、ReentrantLock的lock()方法基於AQS的acquire(int)方法阻塞等待獲取鎖

ReentrantLock中的tryAcquire(int)方法實現:

protected final boolean tryAcquire(int acquires) {      final Thread current = Thread.currentThread();      int c = getState();      // 沒有任何執行緒持有鎖      if (c == 0) {          // 通過CLH隊列的head判斷沒有別的執行緒在比當前更早acquires          // 且基於CAS設置state成功(期望的state舊值為0)          if (!hasQueuedPredecessors() &&              compareAndSetState(0, acquires)) {              // 設置持有鎖的執行緒為當前執行緒              setExclusiveOwnerThread(current);              return true;          }      }      // 持有鎖的執行緒為當前執行緒      else if (current == getExclusiveOwnerThread()) {          // 僅僅在當前執行緒,單執行緒,不用基於CAS更新          int nextc = c + acquires;          if (nextc < 0)              throw new Error("Maximum lock count exceeded");          setState(nextc);          return true;      }      // 其他執行緒已經持有鎖      return false;  }

AQS的acquire(int)方法實現

public final void acquire(int arg) {          // tryAcquire檢查釋放能獲取成功          // addWaiter 構建CLH的節點對象併入隊          // acquireQueued執行緒阻塞等待      if (!tryAcquire(arg) &&          acquireQueued(addWaiter(Node.EXCLUSIVE), arg))          // acquireQueued返回true,代表執行緒在獲取資源的過程中被中斷          // 則調用該方法將執行緒中斷標誌位設置為true          selfInterrupt();  }      final boolean acquireQueued(final Node node, int arg) {      // 標記是否成功拿到資源      boolean failed = true;      try {          // 標記等待過程中是否被中斷過          boolean interrupted = false;          // 循環直到資源釋放          for (;;) {              // 拿到前驅節點              final Node p = node.predecessor();                // 如果前驅是head,即本節點是第二個節點,才有資格去嘗試獲取資源              // 可能是head釋放完資源喚醒本節點,也可能被interrupt()              if (p == head && tryAcquire(arg)) {                  // 成功獲取資源                  setHead(node);                  // help GC                  p.next = null;                  failed = false;                  return interrupted;              }                // 需要排隊阻塞等待              // 如果在過程中執行緒中斷,不響應中斷              // 且繼續排隊獲取資源,設置interrupted變數為true              if (shouldParkAfterFailedAcquire(p, node) &&                  parkAndCheckInterrupt())                  interrupted = true;          }      } finally {          if (failed)              cancelAcquire(node);      }  }

(2) 無阻塞嘗試獲取

ReentrantLock中的tryLock()的實現僅僅是非公平鎖實現,實現邏輯基本與tryAcquire一致,不同的是沒有通過hasQueuedPredecessors()檢查CLH隊列的head是否有其他執行緒在等待,這樣當資源釋放時,有執行緒請求資源能插隊優先獲取

ReentrantLock中tryLock()具體實現如下:

public boolean tryLock() {      return sync.nonfairTryAcquire(1);  }    final boolean nonfairTryAcquire(int acquires) {      final Thread current = Thread.currentThread();      int c = getState();      // 沒有任何執行緒持有鎖      if (c == 0) {          // 基於CAS設置state成功(期望的state舊值為0)          // 沒有檢查CLH隊列中是否有執行緒在等待          if (compareAndSetState(0, acquires)) {              setExclusiveOwnerThread(current);              return true;          }      }      // 持有鎖的執行緒為當前執行緒      else if (current == getExclusiveOwnerThread()) {          // 僅僅在當前執行緒,單執行緒,不用基於CAS更新          int nextc = c + acquires;          if (nextc < 0) // overflow,整數溢出              throw new Error("Maximum lock count exceeded");          setState(nextc);          return true;      }      // 其他執行緒已經持有鎖      return false;  }

(3) 指定時間內阻塞等待獲取

基本實現如下:

  • 1、ReentrantLock的tryLock(long, TimeUnit)調用AQS的tryAcquireNanos(int, long)方法
  • 2、AQS的tryAcquireNanos先調用tryAcquire(int)嘗試獲取,獲取不到再調用doAcquireNanos(int, long)方法
  • 3、AQS的doAcquireNanos判斷當前節點是否為head且基於tryAcquire(int)能否獲得資源,如果不能獲得且超時時間大於1微秒,則休眠一段時間後再嘗試獲取

ReentrantLock中的實現如下:

public boolean tryLock(long timeout, TimeUnit unit)          throws InterruptedException {      return sync.tryAcquireNanos(1, unit.toNanos(timeout));  }    public final boolean tryAcquireNanos(int arg, long nanosTimeout)          throws InterruptedException {      // 如果執行緒已經被interrupt()方法設置中斷      if (Thread.interrupted())          throw new InterruptedException();      // 先tryAcquire嘗試獲取鎖      return tryAcquire(arg) ||          doAcquireNanos(arg, nanosTimeout);  }

AQS中的實現如下:

private boolean doAcquireNanos(int arg, long nanosTimeout)          throws InterruptedException {      if (nanosTimeout <= 0L)          return false;      // 獲取到資源的截止時間      final long deadline = System.nanoTime() + nanosTimeout;      final Node node = addWaiter(Node.EXCLUSIVE);      // 標記是否成功拿到資源      boolean failed = true;      try {          for (;;) {              // 拿到前驅節點              final Node p = node.predecessor();              // 如果前驅是head,即本節點是第二個節點,才有資格去嘗試獲取資源              // 可能是head釋放完資源喚醒本節點,也可能被interrupt()              if (p == head && tryAcquire(arg)) {                  // 成功獲取資源                  setHead(node);                  // help GC                  p.next = null;                  failed = false;                  return true;              }              // 更新剩餘超時時間              nanosTimeout = deadline - System.nanoTime();              if (nanosTimeout <= 0L)                  return false;              // 排隊是否需要排隊阻塞等待              // 且超時時間大於1微秒,則執行緒休眠到超時時間到了再嘗試獲取              if (shouldParkAfterFailedAcquire(p, node) &&                  nanosTimeout > spinForTimeoutThreshold)                  LockSupport.parkNanos(this, nanosTimeout);                // 如果執行緒已經被interrupt()方法設置中斷              // 則不再排隊,直接退出              if (Thread.interrupted())                  throw new InterruptedException();          }      } finally {          if (failed)              cancelAcquire(node);      }  }

(4) 響應中斷獲取

ReentrantLock響應中斷獲取鎖的方式是:當執行緒在park方法休眠中響應thead.interrupt()方法中斷喚醒時,檢查到執行緒中斷標誌位為true,主動拋出異常,核心實現在AQS的doAcquireInterruptibly(int)方法中

基本實現與阻塞等待獲取類似,只是調用從AQS的acquire(int)方法,改為調用AQS的doAcquireInterruptibly(int)方法

private void doAcquireInterruptibly(int arg)      throws InterruptedException {      final Node node = addWaiter(Node.EXCLUSIVE);      // 標記是否成功拿到資源      boolean failed = true;      try {          for (;;) {              // 拿到前驅節點              final Node p = node.predecessor();                // 如果前驅是head,即本節點是第二個節點,才有資格去嘗試獲取資源              // 可能是head釋放完資源喚醒本節點,也可能被interrupt()              if (p == head && tryAcquire(arg)) {                  // 成功獲取資源                  setHead(node);                  p.next = null; // help GC                  failed = false;                  return;              }                // 需要排隊阻塞等待              if (shouldParkAfterFailedAcquire(p, node) &&                  // 從排隊阻塞中喚醒,如果檢查到中斷標誌位為true                  parkAndCheckInterrupt())                  // 主動響應中斷                  throw new InterruptedException();          }      } finally {          if (failed)              cancelAcquire(node);      }  }

2 顯式釋放

AQS資源共享方式分為獨佔式和共享式,這裡先以ReentrantLock為例介紹獨佔式資源的顯式釋放,共享式後面會介紹到

與顯式獲取有類似之處,ReentrantLock顯式釋放基本實現如下:

  • 1、ReentrantLock實現AQS的tryRelease(int)方法,方法將state變數減1,如果state變成0代表沒有任何執行緒持有鎖,返回true,否則返回false
  • 2、AQS的release(int)方法基於tryRelease(int)排隊是否有任何執行緒持有資源,如果沒有,則喚醒CLH隊列中頭節點的執行緒
  • 3、被喚醒後的執行緒繼續執行acquireQueued(Node,int)或者doAcquireNanos(int, long)或者doAcquireInterruptibly(int)中for(;;)中的邏輯,繼續嘗試獲取資源

ReentrantLock中tryRelease(int)方法實現如下:

protected final boolean tryRelease(int releases) {      int c = getState() - releases;      // 只有持有鎖的執行緒才有資格釋放鎖      if (Thread.currentThread() != getExclusiveOwnerThread())          throw new IllegalMonitorStateException();        // 標識是否沒有任何執行緒持有鎖      boolean free = false;        // 沒有任何執行緒持有鎖      // 可重入鎖每lock一次都需要對應一次unlock      if (c == 0) {          free = true;          setExclusiveOwnerThread(null);      }      setState(c);      return free;  }

AQS中的release(int)方法實現如下:

public final boolean release(int arg) {      // 嘗試釋放資源      if (tryRelease(arg)) {          Node h = head;          // 頭節點不為空          // 後繼節點入隊後進入休眠狀態之前,會將前驅節點的狀態更新為SIGNAL(-1)          // 頭節點狀態為0,代表沒有後繼的等待節點          if (h != null && h.waitStatus != 0)              // 喚醒第二個節點              // 頭節點是佔用資源的執行緒,第二個節點才是首個等待資源的執行緒              unparkSuccessor(h);          return true;      }      return false;  }

3 可重入

可重入的實現比較簡單,以ReentrantLock為例,主要是在tryAcquire(int)方法中實現,持有鎖的執行緒是不是當前執行緒,如果是,更新同步狀態值state,並返回true,代表能獲取鎖

4 可共享

可共享資源以ReentrantReadWriteLock為例,跟獨佔鎖ReentrantLock的區別主要在於,獲取的時候,多個執行緒允許共享讀鎖,當寫鎖釋放時,多個阻塞等待讀鎖的執行緒能同時獲取到

ReentrantReadWriteLock類中將AQS的state同步狀態值定義為,高16位為讀鎖持有數,低16位為寫鎖持有鎖

ReentrantReadWriteLock中tryAcquireShared(int)、tryReleaseShared(int)實現的邏輯較長,主要涉及讀寫互斥、可重入判斷、讀鎖對寫鎖的讓步,篇幅所限,這裡就不展開了

獲取讀鎖(ReadLock.lock())主要實現如下:

  • 1、ReentrantReadWriteLock實現AQS的tryAcquireShared(int)方法,判斷當前執行緒能否獲得讀鎖
  • 2、AQS的acquireShared(int)先基於tryAcquireShared(int)嘗試獲取資源,如果獲取失敗,則加入CLH隊列排隊阻塞等待
  • 3、ReentrantReadWriteLock的ReadLock.lock()方法基於AQS的acquireShared(int)方法阻塞等待獲取鎖

AQS中共享模式獲取資源的具體實現如下:

public final void acquireShared(int arg) {      // tryAcquireShared返回負數代表獲取共享資源失敗      // 則通過進入等待隊列,直到獲取到資源為止才返回      if (tryAcquireShared(arg) < 0)          doAcquireShared(arg);  }    // 與前面介紹到的acquireQueued邏輯基本一致  // 不同的是將tryAcquire改為tryAcquireShared  // 還有資源獲取成功後將傳播給CLH隊列上等待該資源的節點  private void doAcquireShared(int arg) {      final Node node = addWaiter(Node.SHARED);       // 標記是否成功拿到資源      boolean failed = true;      try {          boolean interrupted = false;          for (;;) {              final Node p = node.predecessor();              if (p == head) {                  int r = tryAcquireShared(arg);                  // 資源獲取成功                  if (r >= 0) {                      // 傳播給CLH隊列上等待該資源的節點                      setHeadAndPropagate(node, r);                      p.next = null; // help GC                      if (interrupted)                          selfInterrupt();                      failed = false;                      return;                  }              }              // 需要排隊阻塞等待              // 如果在過程中執行緒中斷,不響應中斷              // 且繼續排隊獲取資源,設置interrupted變數為true              if (shouldParkAfterFailedAcquire(p, node) &&                  parkAndCheckInterrupt())                  interrupted = true;          }      } finally {          if (failed)              cancelAcquire(node);      }  }    //  資源傳播給CLH隊列上等待該資源的節點  private void setHeadAndPropagate(Node node, int propagate) {      Node h = head;      setHead(node);      if (propagate > 0 || h == null || h.waitStatus < 0 ||          (h = head) == null || h.waitStatus < 0) {          Node s = node.next;          if (s == null || s.isShared())              // 釋放共享資源              doReleaseShared();      }  }

釋放讀鎖(ReadLock.unlock())主要實現如下:
ReentrantReadWriteLock中共享資源的釋放主要實現如下:

  • 1、ReentrantReadWriteLock實現AQS的tryReleaseShared(int)方法,判斷讀鎖釋放後是否還有執行緒持有讀鎖
  • 2、AQS的releaseShared(int)基於tryReleaseShared(int)判斷是否需要CLH隊列中的休眠執行緒,如果需要就執行doReleaseShared()
  • 3、ReentrantReadWriteLock的ReadLock.unlock()方法基於AQS的releaseShared(int)方法釋放鎖

AQS中共享模式釋放資源具體實現如下:

public final boolean releaseShared(int arg) {      // 允許喚醒CLH中的休眠執行緒      if (tryReleaseShared(arg)) {          // 執行資源釋放          doReleaseShared();          return true;      }      return false;  }    private void doReleaseShared() {      for (;;) {          Node h = head;          if (h != null && h != tail) {              int ws = h.waitStatus;              // 當前節點正在等待資源              if (ws == Node.SIGNAL) {                  // 當前節點被其他執行緒喚醒了                  if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))                      continue;                  unparkSuccessor(h);              }              // 進入else的條件是,當前節點剛剛成為頭節點              // 尾節點剛剛加入CLH隊列,還沒在休眠前將前驅節點狀態改為SIGNAL              // CAS失敗是尾節點已經在休眠前將前驅節點狀態改為SIGNAL              else if (ws == 0 &&                       !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))                  continue;          }          // 每次喚醒後驅節點後,執行緒進入doAcquireShared方法,然後更新head          // 如果h變數在本輪循環中沒有被改變,說明head == tail,隊列中節點全部被喚醒          if (h == head)              break;      }  }

5 公平、非公平

這個特性實現比較簡單,以ReentrantLock鎖為例,公平鎖直接基於AQS的acquire(int)獲取資源,而非公平鎖先嘗試插隊:基於CAS,期望state同步變數值為0(沒有任何執行緒持有鎖),更新為1,如果全部CAS更新失敗再進行排隊

// 公平鎖實現  final void lock() {      acquire(1);  }    // 非公平鎖實現  final void lock() {      // 第1次CAS      // state值為0代表沒有任何執行緒持有鎖,直接插隊獲得鎖      if (compareAndSetState(0, 1))          setExclusiveOwnerThread(Thread.currentThread());      else          acquire(1);  }    protected final boolean tryAcquire(int acquires) {      return nonfairTryAcquire(acquires);  }    // 在nonfairTryAcquire方法中再次CAS嘗試獲取鎖  final boolean nonfairTryAcquire(int acquires) {      final Thread current = Thread.currentThread();      int c = getState();      if (c == 0) {          // 第2次CAS嘗試獲取鎖          if (compareAndSetState(0, acquires)) {              setExclusiveOwnerThread(current);              return true;          }      }      // 已經獲得鎖      else if (current == getExclusiveOwnerThread()) {          int nextc = c + acquires;          if (nextc < 0) // overflow              throw new Error("Maximum lock count exceeded");          setState(nextc);          return true;      }      return false;  }

總結

AQS的state變數值的含義不一定代表資源,不同的AQS的繼承類可以對state變數值有不同的定義

例如在countDownLatch類中,state變數值代表還需釋放的latch計數(可以理解為需要打開的門閂數),需要每個門閂都打開,門才能打開,所有等待執行緒才會開始執行,每次countDown()就會對state變數減1,如果state變數減為0,則喚醒CLH隊列中的休眠執行緒

學習類似底層源碼建議先定幾個問題,帶著問題學習;通俗學習前建議先理解透徹整體設計,整體原理(可以先閱讀相關文檔資料),再研究和源碼細節,避免一開始就扎進去源碼,容易無功而返

Tags: