@html
State狀態java
FIFO隊列node
獲取/釋放的方法安全
若是沒有AQS:數據結構
就須要每一個協做工具直接實現併發
線程的阻塞與解除阻塞框架
隊列的管理ide
AbstractQueuedSynchronizer 是 Doug Lea 寫的,從JDK1.5加入的一個基於FIFO等待隊列實現的一個用於實現同步器的基礎框架,我沒用IDE看AQS的實現類,能夠發現實現類有這些實現函數
在這裏插入圖片描述AQS三大核心:工具
/** * The synchronization state. * 線程同步狀態 利用來volatile 保證可見性 */ private volatile int state;
/** * Atomically sets synchronization state to the given updated * value if the current state value equals the expected value. * This operation has memory semantics of a {@code volatile} read * and write. * * 利用了 unsafe 類的 cas操做 * * @param expect the expected value * @param update the new value * @return {@code true} if successful. False return indicates that the actual * value was not equal to the expected value. */ protected final boolean compareAndSetState(int expect, int update) { // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }
像在 ReentrantLock中
這個隊列用來存放 「等待的線程」,AQS就是 「排對管理器」,當多個線程爭用同一把鎖的時候,必須有排隊機制講那些沒能拿到鎖的線程串在一塊兒。當鎖釋放時,鎖管理器就會挑選一個合適的線程佔有這個剛剛釋放的鎖
這裏的獲取和釋放方法,是利用AQS的協做工具類裏最重要的方法,是由協做類本身實現的,而且含義各不相同
獲取:
獲取操做會依賴state變量,常常會阻塞(好比獲取不到鎖的時候)
在Semaphore 中 ,獲取就是acquire 方法,做用就是獲取一個許可證
而在CountDownLatch裏,獲取就是await方法,做用等待,直到倒數結束
釋放:
咱們看下 CountDownLatch的源碼
/** * 構造函數 , 傳入int值,判斷小於0 給Sync 賦值 */ public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); }
/*** Sync 繼承類AQS Sync的構造獲*/private static final class Sync extends AbstractQueuedSynchronizer {
Sync(int count) { setState(count); }
/** *把AQS state值設置 newState */ protected final void setState(int newState) { state = newState; }
/** * 調用以下 獲取AQS類的 * private volatile int state; */
protected final int getState() { return state; }
// 調用AQS 的getState()方法int getCount() { return getState(); }
//AQS返回state的值protected final int getState() { return state;}
//進行等待 調用AQS sync.acquireSharedInterruptibly(1); public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); }
public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); //若是小於0 則放入等待隊列中 if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg); }
private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { //包裝成Node節點 final Node node = addWaiter(Node.SHARED); boolean failed = true; try { //死循環 for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && //中斷線程的方法 parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
//park 調用的 LockSupport.park(this);private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
// 減一減一 直到0開始喚醒 public void countDown() { sync.releaseShared(1); }
public final boolean releaseShared(int arg) { //判斷是否等於0 if (tryReleaseShared(arg)) { // 喚醒全部線程 doReleaseShared(); return true; } return false; }
調用CountDownLatch的await方法時,便會嘗試獲取 "共享鎖",不過一開始是獲取不到鎖的,因而線程阻塞/
而 「共享鎖」 可獲取到的條件,就是 「鎖計數器」 的值爲0。
而 「鎖計數器初始值爲count」 沒當一個線程調用 才減一
當count 線程調用countDown()以後 , 「鎖計數器」 才爲 0,而前面提到的等待獲取共享個i昂鎖的線程才能繼續運行
美團技術團隊《從ReentrantLock的實現看AQS的原理及應用》:https://mp.weixin.qq.com/s/sA01gxC4EbgypCsQt5pVog.
老錢《打通 Java 任督二脈 —— 併發數據結構的基石》:https://juejin.im/post/5c11d6376fb9a049e82b6253.
HongJie《一行一行源碼分析清楚AbstractQueuedSynchronizer》:https://javadoop.com/post/AbstractQueuedSynchronizer.
愛吃魚的KK《AbstractQueuedSynchronizer 源碼分析 (基於Java 8)》:https://www.jianshu.com/p/e7659436538b.
waterystone《Java併發之AQS詳解》:https://www.cnblogs.com/waterystone/p/4920797.html.
英文論文的中文翻譯:https://www.cnblogs.com/dennyzhangdd/p/7218510.html.
AQS做者的英文論文:http://gee.cs.oswego.edu/dl/papers/aqs.pdf.