99精品伊人亚洲|最近国产中文炮友|九草在线视频支援|AV网站大全最新|美女黄片免费观看|国产精品资源视频|精彩无码视频一区|91大神在线后入|伊人终合在线播放|久草综合久久中文

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

AQS是什么

科技綠洲 ? 來源:Java技術(shù)指北 ? 作者:Java技術(shù)指北 ? 2023-10-13 14:54 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

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

圖片

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

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

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

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

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

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

好了,現(xiàn)在隨著指北君開始第一篇的學(xué)習(xí)吧

AQS是什么

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

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

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

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

第一板斧:狀態(tài)

private volatile int state;

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

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

第二板斧:隊列

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

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

指北君已經(jīng)在源碼上做了詳細(xì)的注釋,但這幾個關(guān)鍵的屬性上還是提出來單獨看看。

下面四個屬性是和節(jié)點相關(guān)的:

  1. prev:雙向鏈表的前驅(qū)節(jié)點
  2. next:雙向鏈表的后繼節(jié)點
  3. thread:節(jié)點所代表的線程
  4. waitStatus:該節(jié)點線程所處的狀態(tài),即等待鎖的狀態(tài)

下面四個屬性是waitStatus的具體狀態(tài):

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

關(guān)于waitStatus有幾個點需要注意下:

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

第三板斧:CAS

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

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

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

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

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;

// 靜態(tài)代碼塊,初始化五個偏移量
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對其操作能保證其線程安全,因此也可以猜到這些屬性肯定是多線程爭著修改的目標(biāo)。靜態(tài)塊里則是對這五個屬性偏移量進(jìn)行初始化。

總結(jié)

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

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

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 源碼
    +關(guān)注

    關(guān)注

    8

    文章

    671

    瀏覽量

    30347
  • 容器
    +關(guān)注

    關(guān)注

    0

    文章

    511

    瀏覽量

    22458
  • 模型
    +關(guān)注

    關(guān)注

    1

    文章

    3521

    瀏覽量

    50437
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點推薦

    HMC253AQS24 Gerber Files

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

    HMC241AQS16 S-Parameters

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

    HMC245AQS16 Gerber Files

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

    HMC241AQS16革資料

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

    HMC253AQS24 S參數(shù)

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

    HMC241AQS16 S參數(shù)

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

    HMC253AQS24革資料

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

    HMC253AQS24 S參數(shù)

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

    HMC241AQS16 S參數(shù)

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

    HMC245AQS16E S參數(shù)

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

    HMC245AQS16革資料

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

    HMC241AQS16革資料

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

    AQS的同步組件有哪些呢?

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

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

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

    AQS獨占鎖的獲取

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