那什么是管程呢?所謂管程,就是 管理共享變量以及對(duì)共享變量操作的過程 ,其有三種模型,分別為 Hasen 模型、Hoare 模型和 MESA 模型。目前應(yīng)用最廣泛的是MESA模型,而JAVA采用的也是這種MESA模型(其模型圖如下圖所示):
可能這個(gè)圖大家現(xiàn)在還看不太明白,沒關(guān)系,暫時(shí)留個(gè)印象,當(dāng)看完指北君AQS系列文章以后,你再回過頭來看這個(gè)圖,肯定秒懂!
Java中的synchronized關(guān)鍵字就是其管程的具體實(shí)現(xiàn),當(dāng)然,今天所要聊的AQS同樣也是。AQS是Java并發(fā)編程的基礎(chǔ),只要掌握它,java.util .concurrent工具包下的大部分工具類源碼你都能在10分鐘內(nèi)看懂,連源碼都懂了,還怕不知道怎么用嗎?!
所以,針對(duì)AQS,指北君將用三篇文章來講解:
第一篇即本篇,我會(huì)將AQS做個(gè)整體的介紹,并將其基礎(chǔ)總結(jié)成了三把斧頭,有了這三把斧頭,你就能快速斬獲AQS
第二篇我們則講AQS如何通過鎖機(jī)制解決互斥問題
第三篇?jiǎng)t是AQS如何通過條件變量來解決線程通信協(xié)作問題
好了,現(xiàn)在隨著指北君開始第一篇的學(xué)習(xí)吧
AQS是什么
好了,本文的正篇正式開始。前言說了半天AQS,AQS到底是什么呢?AQS全稱為AbstractQueuedSynchronizer,翻譯成中文即:抽象隊(duì)列同步器,它是java.util .concurrent工具包下的抽象類,也是一個(gè)模板類(設(shè)計(jì)模式中的模板模式可以了解一波),你也可以理解為為開發(fā)人員提供的一種同步框架,它已經(jīng)幫我們實(shí)現(xiàn)了大部分公共通用邏輯,如線程入隊(duì)、出隊(duì),阻塞、喚醒等,我們只需要根據(jù)我們自己的需求,實(shí)現(xiàn)一些特定的方法,這些方法也叫鉤子方法,比如下面幾個(gè)方法:
- tryAcquire:獨(dú)占模式下獲取同步狀態(tài)
- tryRelease:獨(dú)占模式下釋放同步狀態(tài)
- tryAcquireShared:共享模式下獲取同步狀態(tài)
- tryReleaseShared:共享模式下釋放同步狀態(tài)
- isHeldExclusively:獨(dú)占模式下,查看當(dāng)前線程是否獲取同步狀態(tài)
AQS針對(duì)互斥,提供了兩種模式,即獨(dú)占模式和共享模式。獨(dú)占模式只允許一個(gè)線程拿到鎖去操作共享資源,而共享鎖則有多把鎖,允許多個(gè)線程同時(shí)操作共享資源,這個(gè)第二篇會(huì)詳細(xì)講解,在這之前我們需要先了解AQS的三板斧,這三個(gè)是了解AQS的基礎(chǔ):
- 狀態(tài):AQS中的所有邏輯都是依據(jù)狀態(tài)state來進(jìn)行的,所以它是整個(gè)類的和興。它是被關(guān)鍵字volatile修飾的,保證其可見性和部分有序性
- 隊(duì)列:一共有兩種隊(duì)列,同步隊(duì)列和條件隊(duì)列。當(dāng)線程的請(qǐng)求在短時(shí)間內(nèi)得不到滿足時(shí),線程會(huì)被包裝成某種類型的數(shù)據(jù)結(jié)構(gòu)放入隊(duì)列中,當(dāng)條件滿足時(shí)則會(huì)拿出隊(duì)列。
- CAS:由Unsafe工具類來實(shí)現(xiàn)的,其操作具有原子性,AQS通過CAS和volatile來保證狀態(tài)的線程安全
第一板斧:狀態(tài)
private volatile int state;
狀態(tài)是被volatile修飾的int類型變量,它的值代表著鎖還剩多少。在獨(dú)占鎖模式下,只有一把鎖,則state只有0和1兩個(gè)值,1代表鎖沒被其他線程占有,目前可獲??;0則代表鎖已經(jīng)被其他線程占有了。在共享鎖模式下,state最大值就是鎖的數(shù)量。
后面我們對(duì)state的修改都是通過CAS操作,所以是線程安全的。
第二板斧:隊(duì)列
AQS中存在兩種隊(duì)列,一種叫同步隊(duì)列,它是雙向鏈表,其獲取鎖失敗的線程會(huì)被包裝成Node放入同步隊(duì)列中;另一種叫條件隊(duì)列,它是單向鏈表,線程可以調(diào)用等待方法來把自己放入條件隊(duì)列,或者調(diào)用喚醒方法把條件隊(duì)列的其他被包裝成Node的線程移到同步隊(duì)列中。這個(gè)大家如果此時(shí)看不太懂沒關(guān)系,第三篇文章會(huì)詳細(xì)介紹這個(gè)等待喚醒機(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,初始化時(shí),該值為0
volatile int waitStatus;
// 雙向鏈表前指針和后指針
volatile Node prev;
volatile Node next;
// 表示當(dāng)前Node所代表的Thread
volatile Thread thread;
// 如果此屬性為EXCLUSIVE,則為獨(dú)占模式;為SHARED,則為共享模式
Node nextWaiter;
// 判斷是否是共享模式
final boolean isShared() {
return nextWaiter == SHARED;
}
// 獲取鏈表的頭個(gè)節(jié)點(diǎn)
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
// 構(gòu)造函數(shù),一般用在創(chuàng)建head頭結(jié)點(diǎn)時(shí)使用
Node() {
}
// 構(gòu)造函數(shù),在addWaiter方法時(shí)使用
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ì)的注釋,但這幾個(gè)關(guān)鍵的屬性上還是提出來單獨(dú)看看。
下面四個(gè)屬性是和節(jié)點(diǎn)相關(guān)的:
- prev:雙向鏈表的前驅(qū)節(jié)點(diǎn)
- next:雙向鏈表的后繼節(jié)點(diǎn)
- thread:節(jié)點(diǎn)所代表的線程
- waitStatus:該節(jié)點(diǎn)線程所處的狀態(tài),即等待鎖的狀態(tài)
下面四個(gè)屬性是waitStatus的具體狀態(tài):
- CANCELLED:此節(jié)點(diǎn)的線程被取消了
- SIGNAL:此節(jié)點(diǎn)的后繼節(jié)點(diǎn)線程被掛起,需要被喚醒
- CONDITION:此節(jié)點(diǎn)的線程在等待信號(hào),也表明當(dāng)前節(jié)點(diǎn)不在同步隊(duì)列中,而在條件隊(duì)列中
- PROPAGATE:此節(jié)點(diǎn)下一個(gè)acquireShared應(yīng)該無條件傳播
關(guān)于waitStatus有幾個(gè)點(diǎn)需要注意下:
- waitStatus除了上面四個(gè)狀態(tài),還有一個(gè)隱式的狀態(tài)為0,即在Node初始化的時(shí)候
- 在獨(dú)占鎖模式下,只會(huì)有到狀態(tài)CANCELLED和SIGNAL。需要特別注意的是,SIGNAL它代表的不是自己線程的狀態(tài),而是它后繼節(jié)點(diǎn)的狀態(tài),當(dāng)一個(gè)節(jié)點(diǎn)waitStatus為SIGNAL時(shí),意味著此節(jié)點(diǎn)的后繼節(jié)點(diǎn)被掛起,當(dāng)此節(jié)點(diǎn)釋放鎖或者被取消拿鎖,應(yīng)該要喚醒后繼節(jié)點(diǎn)
- 在共享鎖模式下,只會(huì)用到狀態(tài)CANCELLED和PROPAGATE
第三板斧:CAS
CAS又叫比較交換操作,它是Unsafe類中compareAndSwapXXX方法,我通過下面的例子做個(gè)簡單的介紹:
unsafe.compareAndSwapObject(this, tailOffset, expect, update);
CAS執(zhí)行時(shí),會(huì)拿地址tailOffset上的值與expect做比較,如果相同,則會(huì)將地址上的值更新為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)代碼塊,初始化五個(gè)偏移量
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個(gè)方法都是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);
}
我們說第二板斧的時(shí)候說過,其五個(gè)屬性state、head、tail,waitStatus、next都是被volatile修飾的,所以CAS對(duì)其操作能保證其線程安全,因此也可以猜到這些屬性肯定是多線程爭著修改的目標(biāo)。靜態(tài)塊里則是對(duì)這五個(gè)屬性偏移量進(jìn)行初始化。
總結(jié)
AQS的三板斧就介紹完啦,我們?cè)賮砗唵位仡櫹拢?/p>
AQS(AbstractQueuedSynchronizer)是java.util .concurrent工具包下的抽象類,它通過實(shí)現(xiàn)MESA管程來解決并發(fā)領(lǐng)域中的同步與互斥問題。AQS實(shí)現(xiàn)中最重要的三點(diǎn)就是狀態(tài)、CAS和隊(duì)列,我們也稱之為AQS的三板斧。AQS的一切操作都是依據(jù)狀態(tài)state來的,它是被volatile修飾的全局變量,因此我們通過CAS操作使其線程安全。隊(duì)列是維護(hù)阻塞等待線程的容器,所有未獲得鎖或被要求等待的線程都會(huì)被包裝成Node放入隊(duì)列中。
-
源碼
+關(guān)注
關(guān)注
8文章
643瀏覽量
29251 -
容器
+關(guān)注
關(guān)注
0文章
495瀏覽量
22069 -
模型
+關(guān)注
關(guān)注
1文章
3254瀏覽量
48881
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論