在之前的文章中,我們介紹了 ReentrantLock、ReadWriteLock、CountDownLatch、CyclicBarrier、Semaphore、ThreadPoolExecutor 等并發工具類的使用方式,它們在請求共享資源的時候,都能實現線程同步的效果。
在使用方式上稍有不同,有的是獨占式,多個線程競爭時只有一個線程能執行方法,比如 ReentrantLock 等;有的是共享式,多個線程可以同時執行方法,比如:ReadWriteLock、CountDownLatch、Semaphore 等,不同的實現爭用共享資源的方式也不同。
如果仔細閱讀源碼,會發現它們都是基于AbstractQueuedSynchronizer這個抽象類實現的,我們簡稱 AQS。
AQS 是一個提供了原子式管理同步狀態、阻塞和喚醒線程功能的框架,是除了 Java 自帶的synchronized關鍵字之外的鎖實現機制。
可以這么說,AQS是JUC包下線程同步類的基石,也是很多面試官喜歡提問的話題,掌握AQS原理對我們深入理解線程同步技術有著非常重要的意義。
本文以ReentrantLock作為切入點,來解讀AQS相關的知識點,最后配上簡單的應用示例來幫助大家理解 AQS,如果有描述不對的地方,歡迎大家留言指出,不勝感激!
在之前的線程系列文章中,我們介紹了ReentrantLock的基本用法,它是一個可重入的互斥鎖,它具有與使用synchronized關鍵字一樣的效果,并且功能更加強大,編程更加靈活,支持公平鎖和非公平鎖兩種模式。
使用方式也非常簡單,只需要在相應的代碼上調用加鎖和釋放鎖方法即可,簡單示例如下!
public class Counter { // 默認非公平鎖模式 private final Lock lock = new ReentrantLock(); public void add() { // 加鎖 lock.lock(); try { // 具體業務邏輯... } finally { // 釋放鎖 lock.unlock(); } }}
如果閱讀lock()和unlock()方法,會發現它的底層都是由AQS來實現的。
下面,我們一起來看看這兩個方法的源碼實現,本文源碼內容摘取自 JDK 1.8 版本,可能不同的版本略有區別!
public class ReentrantLock implements Lock, java.io.Serializable { // 同步鎖實現類 private final Sync sync; public ReentrantLock() { // 默認構造方法為非公平鎖實現類 sync = new NonfairSync(); } public ReentrantLock(boolean fair) { // true:公平鎖實現類,false:非公平鎖實現類 sync = fair ? new FairSync() : new NonfairSync(); } public void lock() { // 加鎖操作 sync.lock(); } // 非公平鎖實現類 static final class NonfairSync extends Sync { // 加鎖操作 final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } } // 公平鎖實現類 static final class FairSync extends Sync { // 加鎖操作 final void lock() { acquire(1); } } // 公平鎖和非公平鎖,都繼承自 AQS abstract static class Sync extends AbstractQueuedSynchronizer { // lock 抽象方法 abstract void lock(); }}
從源碼上可以清晰的看到,當初始化ReentrantLock對象時,需要指定鎖的模式。
默認構造方法是非公平鎖模式,采用的是NonfairSync內部實現類;公平鎖模式下,則采用的是FairSync內部實現類;這兩個內部實現類都繼承了Sync抽象類;同時,Sync也繼承了AbstractQueuedSynchronizer,也就是我們上文提到的AQS。
如果把lock()方法的請求鏈路進行抽象,可以用如下圖進行簡要概括。
圖片
無論是非公平鎖模式還是公平鎖模式,可能最終都會調用AQS的acquire()方法,它表示通過獨占式的方式加鎖,我們繼續往下看這個方法的源碼,部分核心代碼如下:
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable { // 通過獨占式的方式加鎖 public final void acquire(int arg) { // 嘗試加鎖,會回調具體的實現類 if (!tryAcquire(arg) && // 如果嘗試加鎖失敗,將當前線程加入等待隊列 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } // 由子類完成加鎖邏輯的實現,支持重寫該方法 protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); }}
從AQS的源碼上可以看出,acquire()方法并不進行具體加鎖邏輯的實現,而是通過具體的實現類重寫tryAcquire()方法來完成加鎖操作,如果加鎖失敗,會將當前線程加入等待隊列。
如果是非公平鎖模式,會回調ReentrantLock類的NonfairSync.tryAcquire()方法;如果是公平鎖模式,會回調ReentrantLock類的FairSync.tryAcquire()方法,我們繼續回看ReentrantLock類的源碼。
非公平鎖NonfairSync靜態內部實現類,相關的源碼如下!
// 非公平鎖實現類static final class NonfairSync extends Sync { // 加鎖操作 final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } // 嘗試非公平方式加鎖,重寫父類 tryAcquire 方法 protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 采用CAS方式修改線程同步狀態,如果成功返回true if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { // 支持當前線程,重復獲得鎖,將state值加1 int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }}
公平鎖FairSync靜態內部實現類,相關的源碼如下!
// 公平鎖實現類static final class FairSync extends Sync { // 加鎖操作 final void lock() { acquire(1); } // 嘗試公平方式加鎖,重寫父類 tryAcquire 方法 protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 1)判斷等待隊列是否有線程處于等待狀態,如果沒有,嘗試獲取鎖;如果有,就進入等待隊列 // 2)采用CAS方式修改線程同步狀態,如果成功返回true if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { // 支持當前線程,重復獲得鎖,將state值加1 int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }}
從源碼上可以清晰的看到,無論是是公平鎖還是非公平鎖模式,都是采用compareAndSetState()方法(簡稱CAS)進行加鎖,如果成功就返回true;同時支持當前線程重復獲得鎖,也就是之前提到的鎖可重入機制。
唯一的區別在于:公平鎖實現類多了一個hasQueuedPredecessors()方法判斷,它的用途是判斷等待隊列是否有線程處于等待狀態,如果沒有,嘗試獲取鎖;如果有,就將當前線程存入等待隊列,依此排隊,從而保證線程通過公平方式獲取鎖的目的。
關于 CAS 實現原理,在之前的并發原子類文章中已經有所介紹,通過它加上volatile修飾符可以實現一個無鎖的線程安全訪問操作,本文不再重復解讀,有興趣的朋友可以翻閱之前的文章。
public class ReentrantLock implements Lock, java.io.Serializable { // 同步鎖實現類 private final Sync sync; public void unlock() { // 釋放鎖操作 sync.release(1); }}
unlock()方法的釋放鎖實現相對來說就簡單多了,整個請求鏈路可以用如下圖進行簡要概括。
當調用unlock()方法時,會直接跳轉到AQS的release()方法上,AQS相關的源碼如下!
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable { // 釋放鎖操作 public final boolean release(int arg) { // 嘗試釋放鎖 if (tryRelease(arg)) { // 從隊列頭部中獲取一個等待線程,并進行喚醒操作 Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; } // 由子類完成釋放鎖邏輯的實現,支持重寫該方法 protected boolean tryRelease(int arg) { throw new UnsupportedOperationException(); }}
與加鎖操作類似,AQS的release()方法并不進行具體釋放鎖邏輯的實現,而是通過具體的實現類重寫tryRelease()方法來完成釋放鎖操作,如果釋放鎖成功,會從隊列頭部中獲取一個等待線程,并進行喚醒操作。
我們繼續回看ReentrantLock類的Sync.tryRelease()釋放鎖方法,部分核心源碼如下:
abstract static class Sync extends AbstractQueuedSynchronizer { // 嘗試釋放鎖 protected final boolean tryRelease(int releases) { // 將state值進行減1操作 int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; }}
相比加鎖過程,釋放鎖要簡單的多,主要是將線程的同步狀態值進行自減操作。
如果仔細的研究 AQS 的源碼,盡管實現上很復雜,但是也有規律可循。
從上到下,整個框架可以分為五層,架構可以用如下圖來描述!(圖片來自ReentrantLock 的實現看 AQS 的原理及應用 - 美團技術團隊)
圖片
當有自定義線程同步器接入AQS時,只需要按需重寫第一層的方法即可,不需要關心底層的實現。
以加鎖為例,當調用AQS的 API 層獲取鎖方法時,會先嘗試進行加鎖操作(具體邏輯由實現類完成),如果加鎖失敗,會進入等待隊列處理環節,這些處理邏輯同時也依賴最底層的基礎數據提供層來完成。
整個AQS實現線程同步的核心思想,可以用如下這段話來描述!
AQS 內部維護一個共享資源變量和線程等待隊列,如果被請求的共享資源空閑,那么就將當前請求資源的線程設置為有效的工作線程,將共享資源設置為鎖定狀態;如果共享資源被占用,就需要一定的阻塞等待喚醒機制來保證鎖分配。這個機制主要用的是 CLH 隊列的變體實現的,將暫時獲取不到鎖的線程加入到等待隊列中,待條件允許的時候將線程從隊列中取出并進行喚醒。
CLH 隊列是一個單向鏈表隊列,對應的還有 CLH 鎖實現,它是一個基于邏輯隊列非線程饑餓的一種自旋公平鎖實現,由 Craig、Landin 和 Hagersten 三位大佬發明,因此命名為 CLH 鎖。關于這方面的技術知識講解可以參閱這篇文章:多圖詳解 CLH 鎖的原理與實現。
而AQS中的隊列采用的是 CLH 變體的虛擬雙向隊列,通過將每一條請求共享資源的線程封裝成一個 CLH 隊列的一個節點來實現鎖的分配。
具體實現原理,可以用如下圖來簡單概括:
圖片
同時,AQS中維護了一個共享資源變量state,通過它來實現線程的同步狀態控制,這個字段使用了volatile關鍵字修飾符來保證多線程下的可見性。
當多個線程嘗試獲取鎖時,會通過CAS方式來修改state值,當state=1時表示當前對象鎖已經被占有(相對獨占模式來說),此時其他線程來加鎖時會失敗,加鎖失敗的線程會被放入上文說到的FIFO等待隊列中,并且線程會被掛起,等待其他獲取鎖的線程釋放鎖才能夠被喚醒。
總結下來,用大白話說就是,AQS是基于 CLH 隊列,使用volatile修飾共享變量state,線程通過CAS方式去改變state狀態值,如果成功則獲取鎖成功,失敗則進入等待隊列,等待被喚醒的線程同步器框架。
打開 ReentrantLock、ReadWriteLock、CountDownLatch、CyclicBarrier、Semaphore 等類的源碼實現,你會發現它們的線程同步狀態都是基于AQS實現的,可以看成是AQS的衍生物。
下面我們一起來看看相關的源碼實現!
AQS源碼中維護的共享資源變量state,表示同步狀態的意思,它是實現線程同步控制的關鍵字段,核心源碼如下:
/** * The synchronization state. */private volatile int state;
針對state字段值的獲取和修改,AQS提供了三個方法,并且都采用Final修飾,意味著子類無法重寫它們,相關方法如下:
方法 | 描述 |
protected final int getState() | 獲取 |
protected final void setState(int newState) | 設置 |
protected final boolean compareAndSetState(int expect, int update) | 使用 CAS 方式更新 |
如果仔細分析源碼,state字段還有一個很大的用處,通過它可以實現多線程的獨占模式和共享模式。
以ReentrantLock和Semaphore類為例,它們的加鎖過程中state值的變化情況如下。
圖片
ReentrantLock類部分核心源碼,實現邏輯如下:
public class ReentrantLock implements Lock, java.io.Serializable { // 非公平鎖實現類 static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; // 加鎖操作 final void lock() { // 將state從0設置為1,如果成功,直接獲取當前共享資源 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else // 嘗試加鎖,會轉調tryAcquire方法 acquire(1); } protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); // 判斷state是否等于0 if (c == 0) { // 嘗試state從0設置為1,如果成功,返回true if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { // 支持當前線程可重入,每調用一次,state的值加1 int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } }}
圖片
Semaphore類部分核心源碼,實現邏輯如下:
public class Semaphore implements java.io.Serializable { // 初始化的時候,設置線程最大并發數,本質設置的是state的值 public Semaphore(int permits) { sync = new NonfairSync(permits); } // 非公平鎖內部實現類 static final class NonfairSync extends Sync { NonfairSync(int permits) { // 設置state的值 setState(permits); } // 通過共享方式,嘗試獲取鎖 protected int tryAcquireShared(int acquires) { return nonfairTryAcquireShared(acquires); } } // 嘗試獲取共享資源,會調用Sync.nonfairTryAcquireShared方法 public boolean tryAcquire() { // 如果state的值小于0,表示無可用共享資源 return sync.nonfairTryAcquireShared(1) >= 0; } // 抽象同步類 abstract static class Sync extends AbstractQueuedSynchronizer { // 通過共享方式,嘗試獲取鎖 final int nonfairTryAcquireShared(int acquires) { for (;;) { // 通過cas方式,設置state自減 int available = getState(); int remaining = available - acquires; if (remaining < 0 || compareAndSetState(available, remaining)) return remaining; } } }}
在上文的ReentrantLock源碼分析過程中,對于公平鎖和非公平鎖實現,其實已經有所解讀。
在AQS中所有的加鎖邏輯是有具有的實現類來完成,以ReentrantLock類為例,它的加鎖邏輯由兩個實現類來完成,分別是非公平鎖靜態內部實現類NonfairSync和公平鎖靜態內部實現類FairSync。
如上文的源碼介紹,這兩個類的的加鎖邏輯基本一致,唯一的區別在于:公平鎖實現類加鎖時,增加了一個hasQueuedPredecessors()方法判斷,這個方法會判斷等待隊列是否有線程處于等待狀態,如果沒有,嘗試獲取鎖;如果有,就進入等待隊列。
簡單的說就是,非公平鎖實現類的加鎖方式,如果有線程嘗試獲取鎖,直接嘗試通過CAS方式進行搶鎖,如果搶成功了,就直接獲取鎖,沒有搶成功就進入等待隊列;而公平鎖實現類的加鎖方式,會判斷等待隊列是否有線程處于等待狀態,如果有則不去搶鎖,乖乖排到后面,如果沒有則嘗試搶鎖。
相對來說,非公平鎖會有更好的性能,因為它的吞吐量比較大。其次,非公平鎖讓獲取鎖的時間變得更加不確定,可能會導致在阻塞隊列中的線程長期處于饑餓狀態。
Semaphore類的公平鎖和非公平鎖實現也類似,擁有兩個靜態內部實現類,源碼就不再解讀了,有興趣的朋友可以自行閱讀。
從ReentrantLock的源碼實現中可以看出,AQS使用了模板方法設計模式,它不提供加鎖和釋放鎖的具體邏輯實現,而是由實現類重寫對應的方法來完成,這樣的好處就是實現更加的靈活,不同的線程同步器可以自行繼承AQS類,然后實現獨屬于自身的加鎖和解鎖功能。
常用的模板方法主要有以下幾個:
方法 | 描述 |
protected boolean isHeldExclusively() | 判斷該線程是否正在獨占資源。只有用到 |
protected boolean tryAcquire(int arg) | 獨占方式。嘗試獲取資源, |
protected boolean tryRelease(int arg) | 獨占方式。嘗試釋放資源, |
protected int tryAcquireShared(int arg) | 共享方式。嘗試獲取資源, |
protected boolean tryReleaseShared(int arg) | 共享方式。嘗試釋放資源, |
通常自定義線程同步器,要么是獨占模式,要么是共享模式。
如果是獨占模式,重寫tryAcquire()和tryRelease()方法即可,比如 ReentrantLock 類。
如果是共享模式,重寫tryAcquireShared()和tryReleaseShared()方法即可,比如 Semaphore 類。
當線程調用tryAcquire()方法獲取鎖失敗之后,就會調用addWaiter()方法,將當前線程加入到等待隊列中去。
addWaiter()方法,部分核心源碼如下:
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable { // 將當前線程加入等待隊列 private Node addWaiter(Node mode) { // 以當前線程構造一個節點,嘗試通過CAS方式插入到雙向鏈表的隊尾 Node node = new Node(Thread.currentThread(), mode); Node pred = tail; if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } // 如果插入沒有成功,則通過enq入隊 enq(node); return node; } // 通過enq入隊 private Node enq(final Node node) { // CAS 自旋方式,直到成功加入隊尾 for (;;) { Node t = tail; if (t == null) { // 隊列為空,創建一個空結點作為head結點,并將tail也指向它 if (compareAndSetHead(new Node())) tail = head; } else { node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }}
我們再來看看Node類節點相關的屬性,部分核心源碼如下:
static final class Node { // 當前節點在隊列中的狀態,狀態值枚舉含義如下: // 0:節點初始化時的狀態 // 1: 表示節點引用線程由于等待超時或被打斷時的狀態 // -1: 表示當隊列中加入后繼節點被掛起時,其前驅節點會被設置為SIGNAL狀態,表示該節點需要被喚醒 // -2:當節點線程進入condition隊列時的狀態 // -3:僅在釋放共享鎖releaseShared時對頭節點使用 volatile int waitStatus; // 前驅節點 volatile Node prev; // 后繼節點 volatile Node next; //該節點的線程實例 volatile Thread thread; // 指向下一個處于Condition狀態的節點(用于條件隊列) Node nextWaiter; //...}
可以很清晰的看到,每個關鍵屬性變量都加了volatile修飾符,確保多線程環境下可見。
正如上文所介紹的,Node其實是一個雙向鏈表數據結構,大致的數據結構圖如下!(圖片來自ReentrantLock 的實現看 AQS 的原理及應用 - 美團技術團隊)
圖片
其中第一個節點,也叫頭節點,為虛節點,并不存儲任何線程信息,只是占位用;真正有數據的是從第二個節點開始,當有線程需要加入等待隊列時,會向隊尾進行插入。
線程加入等待隊列之后,會再次調用acquireQueued()方法,嘗試進行獲取鎖,如果成功或者中斷就退出,部分核心源碼如下:
final boolean acquireQueued(final Node node, int arg) { // 標記是否成功拿到鎖 boolean failed = true; try { // 標記等待過程中是否中斷過 boolean interrupted = false; // 開始自旋,要么獲取鎖,要么中斷 for (;;) { // 獲取當前節點的前驅節點 final Node p = node.predecessor(); // 如果p是頭結點,說明當前節點在等待隊列的頭部,嘗試獲取鎖(頭結點是虛節點) if (p == head && tryAcquire(arg)) { // 獲取鎖成功,頭指針移動到當前node setHead(node); p.next = null; // help GC failed = false; return interrupted; } // 如果p不是頭節點或者是頭節點但獲取鎖失敗,判斷當前節點是否要進入阻塞,如果滿足要求,就通過park讓線程進入阻塞狀態,等待被喚醒 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) // 如果沒有成功獲取鎖(比如超時或者被中斷),那么取消節點在隊列中的等待 cancelAcquire(node); }}
線程加入等待隊列實現,總結下來,大致步驟如下:
當線程調用tryRelease()方法釋放鎖成功之后,會從等待隊列的頭部開始,獲取排隊的線程,并進行喚醒操作。
釋放鎖方法,部分核心源碼如下:
public final boolean release(int arg) { // 嘗試釋放鎖 if (tryRelease(arg)) { // 獲取頭部節點 Node h = head; if (h != null && h.waitStatus != 0) // 嘗試喚醒頭部節點的下一個節點中的線程 unparkSuccessor(h); return true; } return false;}
其中unparkSuccessor()是執行喚醒線程的核心方法,部分核心源碼如下:
private void unparkSuccessor(Node node) { // 獲取頭結點 waitStatus int ws = node.waitStatus; // 置零當前線程所在的結點狀態,允許失敗 if (ws < 0) compareAndSetWaitStatus(node, ws, 0); // 獲取當前節點的下一個節點s Node s = node.next; // 如果下個節點是null或者被取消,就從隊列尾部依此尋找節點 if (s == null || s.waitStatus > 0) { s = null; // 從尾部節點開始向前找,找到隊列中排在最前的有效節點 for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } // 將當前節點的下一個節點中的線程,進行喚醒操作 if (s != null) LockSupport.unpark(s.thread);}
線程從等待隊列中被喚醒實現,總結下來,大致步驟如下:
了解完AQS基本原理之后,按照以上的知識點,我們可以自己實現一個不可重入的互斥鎖線程同步類。
示例代碼如下:
public class MutexLock { // 自定義同步器 private static class Sync extends AbstractQueuedSynchronizer { // 判斷是否鎖定狀態 @Override protected boolean isHeldExclusively() { return getState() == 1; } // 嘗試獲取資源,立即返回。成功則返回true,否則false。 @Override protected boolean tryAcquire(int acquires) { //state為0才設置為1,不支持重入! if (compareAndSetState(0, 1)) { //設置為當前線程獨占資源 setExclusiveOwnerThread(Thread.currentThread()); return true; } return false; } // 嘗試釋放資源,立即返回。成功則為true,否則false。 @Override protected boolean tryRelease(int releases) { // 判斷資源是否已被釋放 if (getState() == 0) throw new IllegalMonitorStateException(); setExclusiveOwnerThread(null); //釋放資源,放棄占有狀態 setState(0); return true; } } // 真正同步類的實現都依賴繼承于AQS的自定義同步器! private final Sync sync = new Sync(); // 獲取鎖,會阻塞等待,直到成功才返回 public void lock() { sync.acquire(1); } // 釋放鎖 public void unlock() { sync.release(1); }}
測試類如下:
public class MutexLockTest { private static int count =0; private static MutexLock lock = new MutexLock(); public static void main(String[] args) throws Exception { final int threadNum = 10; CountDownLatch latch = new CountDownLatch(threadNum); // 創建10個線程,同時對count進行1000相加操作 for (int i = 0; i < threadNum; i++) { new Thread(new Runnable() { @Override public void run() { // 加鎖 lock.lock(); for (int j = 0; j < 1000; j++) { count++; } // 釋放鎖 lock.unlock(); // 線程數減 1 latch.countDown(); } }).start(); } // 等待線程執行完畢 latch.await(); System.out.println("執行結果:" + count); }}
輸出結果:
執行結果:10000
從日志輸出結果可以清晰的看到,執行結果與預期值一致!
本文從ReentrantLock源碼分析到AQS原理解析,進行了一次知識內容的總結,從上文的分析中可以看出,AQS是JUC包下線程同步器實現的基石。
ReentrantLock、ReadWriteLock、CountDownLatch、CyclicBarrier、Semaphore、ThreadPoolExecutor 等并發工具類,線程同步的實現都基于AQS來完成,掌握AQS原理對線程同步的理解和使用至關重要。
AQS原理是面試時熱點話題,希望本篇能幫助到大家!
1.https://www.cnblogs.com/waterystone/p/4920797.html
2.https://tech.meituan.com/2019/12/05/aqs-theory-and-apply.html
3.https://zhuanlan.zhihu.com/p/197840259
4.https://juejin.cn/post/7006895386103119908
本文鏈接:http://www.www897cc.com/showinfo-26-60976-0.html三分鐘帶你搞懂 AQS 原理設計
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com