精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

AQS是什么

科技綠洲 ? 來源:Java技術指北 ? 作者:Java技術指北 ? 2023-10-13 14:54 ? 次閱讀

那什么是管程呢?所謂管程,就是 管理共享變量以及對共享變量操作的過程 ,其有三種模型,分別為 Hasen 模型、Hoare 模型和 MESA 模型。目前應用最廣泛的是MESA模型,而JAVA采用的也是這種MESA模型(其模型圖如下圖所示):

圖片

可能這個圖大家現在還看不太明白,沒關系,暫時留個印象,當看完指北君AQS系列文章以后,你再回過頭來看這個圖,肯定秒懂!

Java中的synchronized關鍵字就是其管程的具體實現,當然,今天所要聊的AQS同樣也是。AQS是Java并發編程的基礎,只要掌握它,java.util .concurrent工具包下的大部分工具類源碼你都能在10分鐘內看懂,連源碼都懂了,還怕不知道怎么用嗎?!

所以,針對AQS,指北君將用三篇文章來講解:

第一篇即本篇,我會將AQS做個整體的介紹,并將其基礎總結成了三把斧頭,有了這三把斧頭,你就能快速斬獲AQS

第二篇我們則講AQS如何通過鎖機制解決互斥問題

第三篇則是AQS如何通過條件變量來解決線程通信協作問題

好了,現在隨著指北君開始第一篇的學習吧

AQS是什么

好了,本文的正篇正式開始。前言說了半天AQS,AQS到底是什么呢?AQS全稱為AbstractQueuedSynchronizer,翻譯成中文即:抽象隊列同步器,它是java.util .concurrent工具包下的抽象類,也是一個模板類(設計模式中的模板模式可以了解一波),你也可以理解為為開發人員提供的一種同步框架,它已經幫我們實現了大部分公共通用邏輯,如線程入隊、出隊,阻塞、喚醒等,我們只需要根據我們自己的需求,實現一些特定的方法,這些方法也叫鉤子方法,比如下面幾個方法:

  1. tryAcquire:獨占模式下獲取同步狀態
  2. tryRelease:獨占模式下釋放同步狀態
  3. tryAcquireShared:共享模式下獲取同步狀態
  4. tryReleaseShared:共享模式下釋放同步狀態
  5. isHeldExclusively:獨占模式下,查看當前線程是否獲取同步狀態

AQS針對互斥,提供了兩種模式,即獨占模式和共享模式。獨占模式只允許一個線程拿到鎖去操作共享資源,而共享鎖則有多把鎖,允許多個線程同時操作共享資源,這個第二篇會詳細講解,在這之前我們需要先了解AQS的三板斧,這三個是了解AQS的基礎:

  1. 狀態:AQS中的所有邏輯都是依據狀態state來進行的,所以它是整個類的和興。它是被關鍵字volatile修飾的,保證其可見性和部分有序性
  2. 隊列:一共有兩種隊列,同步隊列和條件隊列。當線程的請求在短時間內得不到滿足時,線程會被包裝成某種類型的數據結構放入隊列中,當條件滿足時則會拿出隊列。
  3. CAS:由Unsafe工具類來實現的,其操作具有原子性,AQS通過CAS和volatile來保證狀態的線程安全

第一板斧:狀態

private volatile int state;

狀態是被volatile修飾的int類型變量,它的值代表著鎖還剩多少。在獨占鎖模式下,只有一把鎖,則state只有0和1兩個值,1代表鎖沒被其他線程占有,目前可獲取;0則代表鎖已經被其他線程占有了。在共享鎖模式下,state最大值就是鎖的數量。

后面我們對state的修改都是通過CAS操作,所以是線程安全的。

第二板斧:隊列

AQS中存在兩種隊列,一種叫同步隊列,它是雙向鏈表,其獲取鎖失敗的線程會被包裝成Node放入同步隊列中;另一種叫條件隊列,它是單向鏈表,線程可以調用等待方法來把自己放入條件隊列,或者調用喚醒方法把條件隊列的其他被包裝成Node的線程移到同步隊列中。這個大家如果此時看不太懂沒關系,第三篇文章會詳細介紹這個等待喚醒機制。我們來看看線程包裝成Node的Node類:

static final class Node {
    
   // nextWaiter屬性的具體值
   static final Node SHARED = new Node();
   static final Node EXCLUSIVE = null;
 
   // 鎖的狀態
   static final int CANCELLED =  1;
   static final int SIGNAL    = -1;
   static final int CONDITION = -2;
   static final int PROPAGATE = -3;
  
   // 線程所處的等待鎖的狀態:CANCELLED,SIGNAL,CONDITION,PROPAGATE,初始化時,該值為0
   volatile int waitStatus;
 
   // 雙向鏈表前指針和后指針
   volatile Node prev;
   volatile Node next;
  
   // 表示當前Node所代表的Thread
   volatile Thread thread;
 
   // 如果此屬性為EXCLUSIVE,則為獨占模式;為SHARED,則為共享模式
   Node nextWaiter;
 
   // 判斷是否是共享模式
   final boolean isShared() {
       return nextWaiter == SHARED;
   }
 
   // 獲取鏈表的頭個節點
   final Node predecessor() throws NullPointerException {
       Node p = prev;
       if (p == null)
           throw new NullPointerException();
       else
           return p;
   }
   
   // 構造函數,一般用在創建head頭結點時使用
   Node() {    
   }
 
   // 構造函數,在addWaiter方法時使用
   Node(Thread thread, Node mode) {     
       this.nextWaiter = mode;
       this.thread = thread;
   }
  
   // 構造函數,在Condition中使用
   Node(Thread thread, int waitStatus) { 
       this.waitStatus = waitStatus;
       this.thread = thread;
   }
}

指北君已經在源碼上做了詳細的注釋,但這幾個關鍵的屬性上還是提出來單獨看看。

下面四個屬性是和節點相關的:

  1. prev:雙向鏈表的前驅節點
  2. next:雙向鏈表的后繼節點
  3. thread:節點所代表的線程
  4. waitStatus:該節點線程所處的狀態,即等待鎖的狀態

下面四個屬性是waitStatus的具體狀態:

  1. CANCELLED:此節點的線程被取消了
  2. SIGNAL:此節點的后繼節點線程被掛起,需要被喚醒
  3. CONDITION:此節點的線程在等待信號,也表明當前節點不在同步隊列中,而在條件隊列中
  4. PROPAGATE:此節點下一個acquireShared應該無條件傳播

關于waitStatus有幾個點需要注意下:

  1. waitStatus除了上面四個狀態,還有一個隱式的狀態為0,即在Node初始化的時候
  2. 在獨占鎖模式下,只會有到狀態CANCELLED和SIGNAL。需要特別注意的是,SIGNAL它代表的不是自己線程的狀態,而是它后繼節點的狀態,當一個節點waitStatus為SIGNAL時,意味著此節點的后繼節點被掛起,當此節點釋放鎖或者被取消拿鎖,應該要喚醒后繼節點
  3. 在共享鎖模式下,只會用到狀態CANCELLED和PROPAGATE

第三板斧:CAS

CAS又叫比較交換操作,它是Unsafe類中compareAndSwapXXX方法,我通過下面的例子做個簡單的介紹:

unsafe.compareAndSwapObject(this, tailOffset, expect, update);

CAS執行時,會拿地址tailOffset上的值與expect做比較,如果相同,則會將地址上的值更新為update,并返回true,否則直接返回false。

了解了CAS的基本例子后,我們看下AQS中CAS相關的代碼:

private static final Unsafe unsafe = Unsafe.getUnsafe();
// state、head、tail,waitStatus、next的偏移量
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;

// 靜態代碼塊,初始化五個偏移量
static {
   try {
       stateOffset = unsafe.objectFieldOffset
           (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
       headOffset = unsafe.objectFieldOffset
           (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
       tailOffset = unsafe.objectFieldOffset
           (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
       waitStatusOffset = unsafe.objectFieldOffset
           (Node.class.getDeclaredField("waitStatus"));
       nextOffset = unsafe.objectFieldOffset
           (Node.class.getDeclaredField("next"));
 
   } catch (Exception ex) { throw new Error(ex); }
}
 
// 下面5個方法都是CAS操作了
// cas操作 state
protected final boolean compareAndSetState(int expect, int update) {
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

// cas操作 head
private final boolean compareAndSetHead(Node update) {
   return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
 
// cas操作 tail
private final boolean compareAndSetTail(Node expect, Node update) {
   return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
 
// cas操作 waitStatus
private static final boolean compareAndSetWaitStatus(Node node, int expect, int update) {
   return unsafe.compareAndSwapInt(node, waitStatusOffset, expect, update);
}

// cas操作 nextOffset
private static final boolean compareAndSetNext(Node node, Node expect, Node update) {
   return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
}

我們說第二板斧的時候說過,其五個屬性state、head、tail,waitStatus、next都是被volatile修飾的,所以CAS對其操作能保證其線程安全,因此也可以猜到這些屬性肯定是多線程爭著修改的目標。靜態塊里則是對這五個屬性偏移量進行初始化。

總結

AQS的三板斧就介紹完啦,我們再來簡單回顧下:

AQS(AbstractQueuedSynchronizer)是java.util .concurrent工具包下的抽象類,它通過實現MESA管程來解決并發領域中的同步與互斥問題。AQS實現中最重要的三點就是狀態、CAS和隊列,我們也稱之為AQS的三板斧。AQS的一切操作都是依據狀態state來的,它是被volatile修飾的全局變量,因此我們通過CAS操作使其線程安全。隊列是維護阻塞等待線程的容器,所有未獲得鎖或被要求等待的線程都會被包裝成Node放入隊列中。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 源碼
    +關注

    關注

    8

    文章

    633

    瀏覽量

    29140
  • 容器
    +關注

    關注

    0

    文章

    494

    瀏覽量

    22045
  • 模型
    +關注

    關注

    1

    文章

    3172

    瀏覽量

    48715
收藏 人收藏

    評論

    相關推薦

    HMC253AQS24 Gerber Files

    HMC253AQS24 Gerber Files
    發表于 02-19 12:51 ?0次下載
    HMC253<b class='flag-5'>AQS</b>24 Gerber Files

    HMC241AQS16 S-Parameters

    HMC241AQS16 S-Parameters
    發表于 02-19 15:49 ?3次下載
    HMC241<b class='flag-5'>AQS</b>16 S-Parameters

    HMC245AQS16 Gerber Files

    HMC245AQS16 Gerber Files
    發表于 03-11 15:54 ?3次下載
    HMC245<b class='flag-5'>AQS</b>16 Gerber Files

    HMC241AQS16革資料

    HMC241AQS16 Gerber Files
    發表于 03-24 10:12 ?0次下載
    HMC241<b class='flag-5'>AQS</b>16革資料

    HMC253AQS24 S參數

    HMC253AQS24 S參數
    發表于 04-09 14:22 ?2次下載
    HMC253<b class='flag-5'>AQS</b>24 S參數

    HMC241AQS16 S參數

    HMC241AQS16 S參數
    發表于 04-09 14:24 ?1次下載
    HMC241<b class='flag-5'>AQS</b>16 S參數

    HMC253AQS24革資料

    HMC253AQS24革資料
    發表于 05-28 16:51 ?1次下載
    HMC253<b class='flag-5'>AQS</b>24革資料

    HMC253AQS24 S參數

    HMC253AQS24 S參數
    發表于 05-28 17:47 ?1次下載
    HMC253<b class='flag-5'>AQS</b>24 S參數

    HMC241AQS16 S參數

    HMC241AQS16 S參數
    發表于 05-30 11:18 ?0次下載
    HMC241<b class='flag-5'>AQS</b>16 S參數

    HMC245AQS16E S參數

    HMC245AQS16E S參數
    發表于 05-30 20:36 ?1次下載
    HMC245<b class='flag-5'>AQS</b>16E S參數

    HMC245AQS16革資料

    HMC245AQS16革資料
    發表于 06-01 12:30 ?0次下載
    HMC245<b class='flag-5'>AQS</b>16革資料

    HMC241AQS16革資料

    HMC241AQS16革資料
    發表于 06-01 15:13 ?0次下載
    HMC241<b class='flag-5'>AQS</b>16革資料

    AQS的同步組件有哪些呢?

    AQS 的全稱為 Abstract Queued Synchronizer,是在 JUC(java.util.concurrent)下子包中的類。
    的頭像 發表于 03-16 09:42 ?463次閱讀

    AQS如何解決線程同步與通信問題

    我們在第一篇中說到AQS使用的是管程模型,而管程模型是使用條件變量來解決同步通信問題的。條件變量會有兩個方法,喚醒和等待。當條件滿足時,我們會通過喚醒方法將條件隊列中的線程放入第二篇所說的同步隊列中
    的頭像 發表于 10-13 11:23 ?476次閱讀

    AQS獨占鎖的獲取

    AQS提供了兩種鎖,獨占鎖和共享鎖。獨占鎖只有一把鎖,同一時間只允許一個線程獲得鎖;而共享鎖則有多把鎖,同一時間允許多個線程獲得鎖。我們本文主要講獨占鎖。 一. 獨占鎖的獲取 AQS中對獨占鎖的獲取
    的頭像 發表于 10-13 14:51 ?440次閱讀
    <b class='flag-5'>AQS</b>獨占鎖的獲取