一、同步器設(shè)計(jì)
??AQS,全稱為AbstractQueuedSynchronizer(抽象隊(duì)列同步器),是Java多線程顯式鎖(Lock)的底層實(shí)現(xiàn)。同步器的設(shè)計(jì)屬于獨(dú)占模式:資源是獨(dú)占的,一次只能一個(gè)線程獲取。同步器的具體設(shè)計(jì)方案如下:
- 定義一個(gè)變量int state=0,變量表示為被獲取的資源數(shù)量。
- 線程在獲取資源前先檢查state的狀態(tài),如果為0,則修改為1,表示獲取資源成功,否則表示資源已經(jīng)被其他線程占用,此時(shí)線程要堵塞以等待其他線程釋放資源。
- 為了能使得資源釋放后找到那些為了等待資源而堵塞的線程,我們把這些線程保存在FIFO隊(duì)列中。
- 當(dāng)占有資源的線程釋放掉資源后,可以從隊(duì)列中喚醒一個(gè)堵塞的線程,由于此時(shí)資源已經(jīng)釋放,因此這個(gè)被喚醒的線程可以獲取資源并且執(zhí)行。
參考鏈接:JUC解析-AQS(1)
??AQS模型如圖1-1所示:

二、AQS成員變量
??AQS底層實(shí)現(xiàn)是雙向鏈表的數(shù)據(jù)結(jié)構(gòu),通過(guò)查看源碼,其主要包含的成員變量包括:
//狀態(tài)變量state
private volatile int state;
//雙向鏈表表頭
private transient volatile Node head;
//雙向鏈表表尾
private transient volatile Node tail;
??Node類(lèi)源碼如下所示:
static final class Node {
//標(biāo)記一個(gè)結(jié)點(diǎn)(對(duì)應(yīng)的線程)在共享模式下等待
static final Node SHARED = new Node();
// 標(biāo)記一個(gè)結(jié)點(diǎn)(對(duì)應(yīng)的線程)在獨(dú)占模式下等待
static final Node EXCLUSIVE = null;
//waitStatus的值,表示該結(jié)點(diǎn)(對(duì)應(yīng)的線程)已被取消
static final int CANCELLED = 1;
//waitStatus的值,表示后繼結(jié)點(diǎn)(對(duì)應(yīng)的線程)需要被喚醒
static final int SIGNAL = -1;
//waitStatus的值,表示該結(jié)點(diǎn)(對(duì)應(yīng)的線程)在等待某一條件
static final int CONDITION = -2;
//waitStatus的值,表示有資源可用,新head結(jié)點(diǎn)需要繼續(xù)喚醒后繼結(jié)點(diǎn)
static final int PROPAGATE = -3;
volatile int waitStatus; //等待狀態(tài)
volatile Node prev; //前驅(qū)節(jié)點(diǎn)
volatile Node next; //后繼節(jié)點(diǎn)
volatile Thread thread; //節(jié)點(diǎn)對(duì)應(yīng)的線程
Node nextWaiter; //等待隊(duì)列下一個(gè)等待的節(jié)點(diǎn)
//其余省略
... ... ... ...
}
三、acquire & release資源獲取模式
?? acquire是一種以獨(dú)占方式獲取資源,如果獲取到資源,線程直接返回,否則進(jìn)入等待隊(duì)列,直到獲取到資源為止,且整個(gè)過(guò)程忽略中斷的影響。acquire方法如下所示:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
1.tryAcquire()嘗試直接去獲取資源,如果成功則直接返回;
2.addWaiter()將該線程加入等待隊(duì)列的尾部,并標(biāo)記為獨(dú)占模式;
3.acquireQueued()使線程在等待隊(duì)列中獲取資源,一直獲取到資源后才返回。如果在整個(gè)等待過(guò)程中被中斷過(guò),則返回true,否則返回false。
4.如果線程在等待過(guò)程中被中斷過(guò),它是不響應(yīng)的。只是獲取資源后才再進(jìn)行自我中斷selfInterrupt(),將中斷補(bǔ)上。
??release方法會(huì)釋放指定量的資源,如果徹底釋放了(即state=0),它會(huì)喚醒等待隊(duì)列里的其他線程來(lái)獲取資源。release方法源碼如下所示:
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
??如果資源釋放成功,調(diào)用unparkSuccessor(Node)方法喚醒等待隊(duì)列中下一個(gè)線程。這里要注意的是,下一個(gè)線程并不一定是當(dāng)前節(jié)點(diǎn)的next節(jié)點(diǎn),而是下一個(gè)可以用來(lái)喚醒的線程,如果這個(gè)節(jié)點(diǎn)存在,調(diào)用unpark()方法喚醒。unparkSuccessor方法源碼如下所示:
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
參考鏈接:Java技術(shù)之AQS詳解