AQS

@html

  • State狀態java

  • FIFO隊列node

  • 獲取/釋放的方法安全

圖片


1.學習AQS的思路

  • 瞭解原理,提升思路
  • 先了解如何使用,應用場景,再去分析它的結構

2.爲何須要AQS

  • 鎖的協做類共同點:閘門 (和 ReentrantLock、Semaphore類似)
  • 不只 ReentrantLock、Semaphore、像CountDownLatch、都有這樣的協做 (或者同步)功能,其實,他們底層都用了一個共同的基類,這就是  AQS
  • 由於這些協做類有不少工做都是相似的,因此若是提取出一個工具類,那麼就直接就能夠用
  • Semaphore 、CountDownLatch 內部有一個類 Sync , Sync類繼承了AQS
圖片在這裏插入圖片描述

3.AQS的做用

  • 若是沒有AQS:數據結構

  • 就須要每一個協做工具直接實現併發

  • 線程的阻塞與解除阻塞框架

  • 隊列的管理ide

  • AQS是一個用於構建鎖、同步器、協做工具類的工具類(框架)。有了AQS之後,更多的協做工具類均可以很方便得被寫出來
  • 有了AQS,構建線程協做類就容易多了

4.AQS的重要性、地位

AbstractQueuedSynchronizer 是 Doug Lea 寫的,從JDK1.5加入的一個基於FIFO等待隊列實現的一個用於實現同步器的基礎框架,我沒用IDE看AQS的實現類,能夠發現實現類有這些實現函數

圖片在這裏插入圖片描述

5.AQS內部原理解析

AQS三大核心:工具

  • state
  • 控制線程搶鎖和配合的FIFO隊列
  • 指望協做工具類去實現的獲取/釋放等重要方法

State狀態

  • 這裏等state的具體含義,會根據具體實現類的不一樣而不一樣,好比在Semaphore 裏,它表示 「 剩餘的許可證數量」 ,而CountDawnLatch 裏,它表示 「 還須要倒數的數量 」
  • state 是 volatile修飾的,會被併發地修改,因此全部修改state的方法要保證線程安全,好比getState 、setState 以及 compareAandState 操做來讀取和更新這個狀態。這些方法都依賴與j.u.c.atomic包的支持
/**     * 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中

  • state用來表示 「鎖」的佔有狀況,包括可重入鎖計數
  • 當state 的值爲 0的時候,標識改Lock不被任何線程所佔有

FIFO隊列

這個隊列用來存放 「等待的線程」,AQS就是 「排對管理器」,當多個線程爭用同一把鎖的時候,必須有排隊機制講那些沒能拿到鎖的線程串在一塊兒。當鎖釋放時,鎖管理器就會挑選一個合適的線程佔有這個剛剛釋放的鎖

獲取/釋放的方法

這裏的獲取和釋放方法,是利用AQS的協做工具類裏最重要的方法,是由協做類本身實現的,而且含義各不相同

獲取:

  • 獲取操做會依賴state變量,常常會阻塞(好比獲取不到鎖的時候)

  • 在Semaphore 中 ,獲取就是acquire 方法,做用就是獲取一個許可證

  • 而在CountDownLatch裏,獲取就是await方法,做用等待,直到倒數結束

釋放:

  • 釋放操做不會被阻塞
  • 在Semaphore中 ,釋放就是 release 方法,做用是釋放一個許可證
  • CountDownLatch 裏面,獲取就是countDown 方法,做用是 「倒數一個數」

6.應用實例,源碼解析

咱們看下 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;    }
  • getCount() 方法

// 調用AQS 的getState()方法int getCount() {            return getState(); }
//AQS返回state的值protected final int getState() {        return state;}
  • await() 方法
//進行等待 調用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();    }
  • countDown() 方法
// 減一減一 直到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昂鎖的線程才能繼續運行

7.AQS優質學習資源

  • 美團技術團隊《從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.

相關文章
相關標籤/搜索