簡介
AQS 全稱是 AbstractQueuedSynchronizer
挽牢,位于java.util.concurrent.locks
包下面,AQS 提供了一個基于FIFO的隊列和維護了一個狀態(tài)state變量賴表示狀態(tài),可以作為構(gòu)建鎖或者其他相關(guān)同步裝置的基礎(chǔ)框架。AQS 支持兩種模式:共享模式 和 排他模式,當(dāng)它被定義為一個排他模式時,其他線程對其的獲取就被阻止庸娱,而共享模式對于多個線程獲取都可以成功。之所以說它是一個同步基礎(chǔ)框架是因為很多同步類里面都用到了AQS谐算,比如 ReentrantLock 中的內(nèi)部類同步器Sync繼承至AQS熟尉,ReentrantReadWriteLock中的同步器也是繼承至AQS,還有 Semaphore 洲脂、CountDownLatch等都是基于AQS來實現(xiàn)的斤儿。
核心源碼
類結(jié)構(gòu)
AQS 繼承了 AbstractOwnableSynchronizer
, AbstractOwnableSynchronizer 這個類比較簡單恐锦,就一個屬性 private transient Thread exclusiveOwnerThread
往果,用來標(biāo)識當(dāng)前獨占鎖的持有者線程,通俗的說就是哪個線程拿到了獨占鎖一铅,就調(diào)用AbstractOwnableSynchronizer 的方法把這個線程保存起來陕贮。源碼如下:
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
...
}
public abstract class AbstractOwnableSynchronizer implements java.io.Serializable {
private transient Thread exclusiveOwnerThread;
// 構(gòu)造方法,get set 方法省略潘飘。飘蚯。馍迄。
}
后面的分析中,會有大量的同步器在獲得鎖之后會調(diào)用setExclusiveOwnerThread(Thread) 方法來保存鎖的持有者線程局骤;
重要內(nèi)部類Node
static final class Node {
volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread;
Node nextWaiter;
}
以上五個成員變量主要負責(zé)保存該節(jié)點的線程引用攀圈,同步隊列的前驅(qū)和后繼節(jié)點,同時也包括了同步狀態(tài)峦甩。
屬性解釋:
waitStatus:表示節(jié)點的狀態(tài)赘来。其中包含的狀態(tài)有:
- CANCELLED,值為1凯傲,表示當(dāng)前的線程被取消犬辰;
- SIGNAL,值為-1冰单,表示當(dāng)前節(jié)點的后繼節(jié)點包含的線程需要運行幌缝,也就是unpark;
- CONDITION诫欠,值為-2涵卵,表示當(dāng)前節(jié)點在等待condition,也就是在condition隊列中荒叼;
- PROPAGATE瞳别,值為-3瘦棋,表示當(dāng)前場景下后續(xù)的acquireShared能夠得以執(zhí)行潮针;
- 值為0谅海,表示當(dāng)前節(jié)點在sync隊列中,等待著獲取鎖嫁乘。
prev:前驅(qū)節(jié)點昆婿,比如當(dāng)前節(jié)點被取消,那就需要前驅(qū)節(jié)點和后繼節(jié)點來完成連接
next:后繼節(jié)點
thread:入隊列時的當(dāng)前線程
nextWaiter:存儲condition隊列中的后繼節(jié)點
重要屬性:同步隊列和同步狀態(tài)
節(jié)點成為同步隊列和 condition 條件隊列構(gòu)建的基礎(chǔ)蜓斧,同步器擁有三個成員變量:頭結(jié)點head
挖诸、尾節(jié)點tail
和同步狀態(tài)state
。
private transient volatile Node head;
private transient volatile Node tail;
private volatile int state;
對于新的獲取鎖請求法精,形成Node節(jié)點,掛載到隊列的尾部痴突;對于鎖資源的釋放都是從隊列的頭部進行操作的搂蜓。
+------+ prev +-----+ +-----+
head | | <---- | | <---- | | tail
+------+ +-----+ +-----+
可以重寫的API
實現(xiàn)自定義同步器時,需要使用同步器提供的getState()辽装、setState()和compareAndSetState()方法來控制同步狀態(tài)帮碰。
方法1:protected boolean tryAcquire(int arg)
描述:已排它模式獲取同步狀態(tài)。這個方法的實現(xiàn)需要查詢前狀態(tài)是否允許獲取拾积,然后再進compareAndSetState()修改狀態(tài)殉挽,修改成功代表成功獲得鎖丰涉。
方法2:protected boolean tryRelease(int arg)
描述:釋放鎖,也就是釋放同步狀態(tài)state的值到初始狀態(tài)斯碌,一般是0一死。
方法3:protected int tryAcquireShared(int arg)
描述:共享模式下獲取同步狀態(tài),一般可以用來做共享鎖傻唾,或者用作限制資源最多同時被訪問多少次投慈。
方法4:protected boolean tryReleaseShared(int arg)
描述:共享模式下釋放同步狀態(tài)。
方法5:protected boolean isHeldExclusively()
描述:在排它模式下冠骄,返回同步狀態(tài)是否被占用伪煤,比如我們可以實現(xiàn)返回邏輯為 getState() == 1,為true的話說明資源已經(jīng)被占用了凛辣。
其他代碼我們通過自己實現(xiàn)簡單的排他鎖案例來進行具體的詳細分析
基于AQS實現(xiàn)的排他鎖
一抱既、定義一個MyAQSLock類
public class MyAQSLock{
}
二、定義一個內(nèi)部類Sync做為同步器扁誓,繼承自AbstractQueuedSynchronizer
public class MyAQSLock{
class Sync extends AbstractQueuedSynchronizer{
}
}
三防泵、重寫同步器部分API
因為我們要實現(xiàn)的是排它鎖的功能,意思就是同一時刻只能有一個線程獲得鎖跋理,所以只需要重寫tryAcquire择克、tryRelease和isHeldExclusively方法即可。
class Sync extends AbstractQueuedSynchronizer{
@Override
protected boolean **tryAcquire**(int acquires){
// 入?yún)⒅荒転?
**assert acquires == 1;
// 使用CAS的方式修改state值前普,修改成功代表成功獲得鎖
if(compareAndSetState(0,1)){
// 修改鎖的持有者為當(dāng)前線程
setExclusiveOwnerThread(Thread.currentThread());
// 返回true肚邢,表示成功獲得鎖
return true;
}
// 返回false,沒有獲得鎖
return false;
}
@Override
protected boolean **tryRelease**(int releases){
assert releases == 1;
if (getState() == 0){
// 已經(jīng)被釋放了
throw new IllegalMonitorStateException();
}
// lock() 和 unlock() 一般都是成對出現(xiàn)的拭卿,所以這里不需要同步語句骡湖,可以直接修改state值為0
setState(0);
return true;
}
@Override
protected boolean **isHeldExclusively**() {
// 返回true,說明已經(jīng)有其他線程獲得鎖
return getState() == 1;
}
}
三峻厚、定義鎖和解鎖方法
public class MyAQSLock{
private final Sync sync;
MyAQSLock(){
sync = new Sync();
}
class Sync extends AbstractQueuedSynchronizer{
...
}
public void lock(){
// 調(diào)用同步器响蕴,獲得鎖
sync.acquire(1);
}
public boolean tryLock(){
// 嘗試獲得鎖,如果沒有獲取到鎖惠桃,則立即返回false
return sync.tryAcquire(1);
}
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException{
// 嘗試獲得鎖浦夷,如果沒有獲取到鎖,允許等待一段時間
return sync.tryAcquireNanos(1,unit.toNanos(timeout));
}
public void unLock(){
// 解鎖
sync.release(1);
}
public boolean isLocked(){
// 判斷鎖是否已經(jīng)被占用
return sync.isHeldExclusively();
}
}
四辜王、測試我們的鎖
static int count = 0;
public static void main(String[] args) throws InterruptedException{
MyAQSLock myAQSLock = new MyAQSLock();
CountDownLatch countDownLatch = new CountDownLatch(1000);
IntStream.range(0,1000).forEach(i->new Thread(()->{
myAQSLock.lock();
try{
IntStream.range(0,10000).forEach(j->{
count++;
});
}finally{
myAQSLock.unLock();
}
countDownLatch.countDown();
}).start());
countDownLatch.await();
System.out.println(count);
}
最后正確輸出10000000劈狐,說明我們實現(xiàn)的鎖是有效的。但是要注意我們自己寫的這個鎖是不支持重入的呐馆。
代碼實現(xiàn)分析
獲得鎖:public void lock()
lock()方法會調(diào)用sync.acquire(int)
方法肥缔,acquire
在AQS里面,方法被final修飾汹来,作為基礎(chǔ)框架邏輯部分续膳,不允許被繼承改艇,源碼展示:
public final void acquire(int arg) {
// tryAcquire 是我們自己實現(xiàn)的方法,具體實現(xiàn)看上面
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// acquireQueued 返回true表示線程被中斷了坟岔,中斷當(dāng)前線程
selfInterrupt();
}
上面acquire()
主要的邏輯有:
嘗試獲得鎖谒兄,調(diào)用
tryAcquire(arg)
方法,該方法的邏輯在我們自定義的MyAQSLock類中炮车,我們利用了compareAndSetState來保證state字段的原子性舵变。如果
tryAcquire
返回true的話,if分支會直接退出瘦穆,表示成功獲得鎖纪隙,繼續(xù)執(zhí)行調(diào)用lock() 方法后面的邏輯;如果
tryAcquire
返回false的話扛或,表示沒有獲得鎖绵咱,會繼續(xù)執(zhí)行 && 后面的邏輯;-
首先會調(diào)用
addWaiter(Node.EXCLUSIVE)
方法為當(dāng)前線程創(chuàng)建排隊節(jié)點熙兔,并加入到隊列悲伶,Node.EXCLUSIVE
代表這個節(jié)點是獨占排他鎖的意思,具體源碼如下:private Node addWaiter(Node mode) { // 為當(dāng)前線程創(chuàng)建一個節(jié)點住涉,最后會返回出去這個節(jié)點 Node node = new Node(Thread.currentThread(), mode); // 隊列不為空時麸锉,快速嘗試在同步隊列尾部添加當(dāng)前節(jié)點,如果失敗了會進入enq方法自旋入隊 Node pred = tail; if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } // 上面入隊失敗了舆声,或者是pred為空(第一個排隊的線程進來)花沉,繼續(xù)自旋入隊 enq(node); return node; } private Node enq(final Node node) { // 空的for循環(huán),自旋操作媳握,直到成功把節(jié)點加入到同步隊列 for (;;) { // 同步隊列尾巴 Node t = tail; if (t == null) { // Must initialize // 尾巴是空的碱屁,還沒有初始化, 第一個排隊的線程進來的話蛾找,隊頭隊尾都是同一個節(jié)點 if (compareAndSetHead(new Node())) tail = head; } else { // 進入到這里娩脾,說明同步隊列已經(jīng)有線程在排隊了 // 當(dāng)前節(jié)點前驅(qū)直接指向同步隊里的尾節(jié)點 node.prev = t; // CAS 修改尾節(jié)點為當(dāng)前節(jié)點 if (compareAndSetTail(t, node)) { // t還是老的尾節(jié)點,修改新的尾節(jié)點后老的尾節(jié)點的下一個節(jié)點就是當(dāng)前節(jié)點打毛,建立他們的聯(lián)系 t.next = node; // 成功把當(dāng)前節(jié)點加入到了同步隊列柿赊,返回當(dāng)前節(jié)點,退出自旋 return t; } } } }
addWaiter()方法總結(jié):首先會快速嘗試一次在隊列的尾部添加當(dāng)前線程節(jié)點幻枉,如果失敗的話(在這個時候碰声,可能有新的線程也沒有獲得鎖,并且跑在當(dāng)前的前面加入到同步隊列了)展辞,會調(diào)用enq邏輯進行自旋加入隊尾,直到成功加入隊列為止万牺。
-
再次嘗試從同步隊列獲得鎖
acquireQueued(node,arg)
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; // 自旋操作 for (;;) { // 當(dāng)前節(jié)點的上一個節(jié)點 final Node p = node.predecessor(); //**①** // 如果前驅(qū)節(jié)點是頭結(jié)點罗珍,然后去嘗試獲得鎖洽腺,tryAcquire是我們自己實現(xiàn)的獲得鎖邏輯 **if (p == head && tryAcquire(arg)) { //②** // 當(dāng)前線程成功獲得鎖,當(dāng)前節(jié)點設(shè)置為頭結(jié)點 setHead(node); p.next = null; // help GC failed = false; // 返回false覆旱,表示沒有被中斷 return interrupted; } // 到這里說明p != head 或者 **tryAcquire** 返回了false蘸朋,還是沒獲得鎖,這時候就需要阻塞線程了 // shouldParkAfterFailedAcquire 如果線程應(yīng)阻塞扣唱,則返回true // parkAndCheckInterrupt 阻塞當(dāng)前線程 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { // 節(jié)點被取消了 if (failed) cancelAcquire(node); } } private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; // SIGNAL值為-1藕坯,表示pred節(jié)點的后繼節(jié)點包含的線程需要運行,也就是unpark if (ws == Node.SIGNAL) return true; if (ws > 0) { // 大于0的值只有1噪沙, 1表示線程被取消 // 進入到這里說明 pred 節(jié)點被取消了炼彪,需要從同步隊列上刪掉它 do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { // 一般初始時為0,設(shè)置成-1 compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } // 返回false正歼,外面會一直自旋操作 return false; } private final boolean parkAndCheckInterrupt() { // 調(diào)用底層的Unsafe一直阻塞線程 LockSupport.park(this); // 被unpark喚醒之后辐马,會繼續(xù)回去自旋獲得鎖,并返回線程在此期間是否有被中斷 return Thread.interrupted(); }
lock方法總結(jié):
- 嘗試獲得鎖局义,方法
tryAcquire
:- 成功獲得鎖喜爷,直接退出;沒有獲得鎖萄唇,繼續(xù)執(zhí)行檩帐;
- 新建排隊節(jié)點,并加入到同步隊列另萤,方法
addWaiter
:- 隊列不為空時湃密,嘗試一次快速直接把節(jié)點加入到隊列尾巴上;如果隊尾為空或者快速添加失敗仲墨,繼續(xù)執(zhí)行下面邏輯
- 自旋勾缭,直到成功把新建的節(jié)點加入到同步隊列;方法
enq
:
為什么要自旋呢目养?是因為在調(diào)這個方法的時候俩由,可能有其他想要獲得鎖線程沒有獲得鎖,并且已經(jīng)修改了尾節(jié)點癌蚁;
- 再次嘗試從同步隊列獲得鎖幻梯,方法
acquireQueued
:- 上面已經(jīng)把當(dāng)前線程的節(jié)點加入到隊列中了,理論上排隊的線程很多的話努释,它是馬上獲取不到鎖的
- 所以它會自旋判斷是否到了自己可以獲取鎖和CAS嘗試獲取鎖碘梢,關(guān)鍵代碼
if (p == head && tryAcquire(arg))
理論上當(dāng)前線程進入到了隊列排隊,只要隊列中還有更早的線程在它前面排隊伐蒂,當(dāng)前線程都不會比更早的線程先獲得鎖煞躬,所以在這一塊對于公平鎖和非公平鎖肯定都是公平的。 - 沒有資格去獲取鎖或沒有成功獲得鎖,就阻塞自己恩沛,方法
parkAndCheckInterrupt
- 阻塞線程被喚醒在扰,自旋成功獲得鎖,排隊期間被中斷的線程也會獲得鎖雷客,之后退出自旋循環(huán)芒珠,返回線程的中斷狀態(tài);
- 線程如果被中斷了搅裙,中斷當(dāng)前線程皱卓,被中斷的線程還是會繼續(xù)執(zhí)行后面邏輯
- 以上過程涉及到的技術(shù)點有:CAS,自旋部逮,隊列入隊娜汁,隊列刪除節(jié)點(被取消的節(jié)點),阻塞線程(LockSupport.park(this))
- 嘗試獲得鎖局义,方法
釋放鎖:public final boolean release(int arg)
unlock方法調(diào)用的是sync.release(1)
甥啄,而release是AQS 方法中的方法存炮,表示將同步狀態(tài)設(shè)置回初始狀態(tài),將鎖釋放蜈漓。
public final boolean release(int arg) {
// tryRelease 是我們自己的實現(xiàn)穆桂,就是把state字段設(shè)置成0,如果是可重入的融虽,只能慢慢減到初始狀態(tài)
if (tryRelease(arg)) {
// 進入到這里說明CAS 設(shè)置成功享完,也就代表鎖成功釋放了,需要喚醒隊列中的第一個排隊的節(jié)點線程
Node h = head;
// head 表示的是當(dāng)前獲得鎖的節(jié)點
if (h != null && h.waitStatus != 0)
// 喚醒頭結(jié)點的下一個節(jié)點
unparkSuccessor(h);
return true;
}
return false;
}
private void unparkSuccessor(Node node) {
// 這里的node 是當(dāng)前持有鎖的節(jié)點
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
// 找到頭結(jié)點的后繼節(jié)點
Node s = node.next;
if (s == null || s.waitStatus > 0) {
// 大于0的狀態(tài)只有1有额,表示被取消了般又,如果被取消了,就繼續(xù)取下一個節(jié)點喚醒
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);**
}
釋放鎖的邏輯比加鎖邏輯要簡單很多巍佑,主要邏輯有:
-
修改同步狀態(tài)為初始值茴迁,方法
tryRelease(arg)
:這里我們自己實現(xiàn)的會直接將同步狀態(tài)設(shè)置為0,如果是支持可重入萤衰,就需要慢慢減了堕义;
釋放鎖儲層,喚醒頭結(jié)點(當(dāng)前獲得鎖的節(jié)點)的后繼節(jié)點:
unparkSuccessor(h);
找到頭結(jié)點的后繼節(jié)點中第一個沒有被取消的節(jié)點脆栋,并喚醒該節(jié)點所處線程
基于AQS實現(xiàn)自己的共享鎖
設(shè)計一個同步器倦卖,在同一時刻,只允許最多兩個線程能夠并行訪問椿争,超過限制的其他線程將進入阻塞狀態(tài)怕膛。
這個功能和 Semaphore 的功能很相似,這個學(xué)會了秦踪,以后看 Semaphore 的源碼也就很簡單了褐捻。
實現(xiàn)思路:
可以利用AQS 的API tryAcquireShared
實現(xiàn)獲得共享鎖掸茅,定義一個狀態(tài),允許的范圍為【0柠逞,1倦蚪,2】,狀態(tài)為2代表新的線程進入的時候需要阻塞等待
public class MyAqsSharedLock{
// 定義最大共享值
private final int maxSharedValue = 2;
// 同步器
private final Sync sync;
MyAqsSharedLock(){
// 構(gòu)造方法初始化同步器
sync = new Sync();
}
// 基于AQS實現(xiàn)的同步器
class Sync extends AbstractQueuedSynchronizer{
@Override
protected int tryAcquireShared(int arg){
// 為什么要自旋呢边苹?因為可能滿足state的條件,但是CAS修改失敗
while(true){
int state = getState();
// 檢查同步狀態(tài)是否達到最大值
if(state >= maxSharedValue){
// 返回-1 表示沒有獲得鎖
return -1;
}
// CAS 修改同步狀態(tài)
if(compareAndSetState(state,state + arg)){
// 修改成功裁僧,表示獲得了鎖个束,大于等于0表示獲得了鎖
return getState();
}
}
}
@Override
protected boolean tryReleaseShared(int arg){
// 為什么要自旋呢?因為可能滿足state的條件聊疲,但是CAS修改失敗
while(true){
int state = getState();
// CAS 修改同步狀態(tài)茬底,修改成功返回true,失敗繼續(xù)自旋
if(compareAndSetState(state,state - arg)){
return true;
}
}
}
}
/** 加鎖 */
public void lock(){
sync.acquireShared(1);
}
/** 解鎖 */
public void unLock(){
sync.releaseShared(1);
}
}
測試方法:
5個線程循環(huán)打印輸出線程名和當(dāng)前時間
public static void main(String[] args){
MyAqsSharedLock lock = new MyAqsSharedLock();
IntStream.range(0,5).forEach(i -> new Thread(new Runnable(){
@SneakyThrows
@Override
public void run(){
while(true){
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+":執(zhí)行获洲。阱表。。時間:"+ LocalDateTime.now());
TimeUnit.SECONDS.sleep(2);
}finally{
lock.unLock();
TimeUnit.SECONDS.sleep(1);
}
}
}
},"T"+i).start());
}
輸出結(jié)果示例:
T0:執(zhí)行贡珊。最爬。。時間:2020-10-30T17:45:45.117
T1:執(zhí)行门岔。爱致。。時間:2020-10-30T17:45:45.117
T3:執(zhí)行寒随。糠悯。。時間:2020-10-30T17:45:47.118
T2:執(zhí)行妻往。互艾。。時間:2020-10-30T17:45:47.118
T1:執(zhí)行讯泣。纫普。。時間:2020-10-30T17:45:49.119
T4:執(zhí)行判帮。局嘁。。時間:2020-10-30T17:45:49.119
會發(fā)現(xiàn)幾乎在同一時間最多只有2個線程在打印輸出晦墙,滿足我們的要求悦昵。
代碼實現(xiàn)分析
獲得共享鎖:public void lock()
共享鎖的 lock() 調(diào)用的是sync.acquireShared(1)
;acquireShared
也在AQS里面晌畅,同樣被final修飾作為基礎(chǔ)框架邏輯部分但指,不允許被繼承,源碼展示:
public final void acquireShared(int arg) {
// tryAcquireShared 是我們自己實現(xiàn)的邏輯,返回-1棋凳,表示沒有獲得鎖
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
// 沒有獲得共享拦坠,再次嘗試獲得鎖,和排他模式的acquireQueued方法非常相似
private void doAcquireShared(int arg) {
// 新建節(jié)點剩岳,加入到隊列贞滨,和排他鎖模式一樣的入隊邏輯
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 當(dāng)前節(jié)點的前驅(qū)節(jié)點
final Node p = node.predecessor();
if (p == head) {
// 前驅(qū)節(jié)點是頭結(jié)點,說明輪到咱獲得鎖了
// 繼續(xù)調(diào)用我們自己的邏輯拍棕,CAS 獲得鎖
int r = tryAcquireShared(arg);
// 這里再次印證了晓铆,我們的tryAcquireShared返回值定義,負值是沒有獲得鎖绰播,>=0 表示成功獲得鎖
if (r >= 0) {
// 設(shè)置新的頭結(jié)點骄噪,如果后面的排隊節(jié)點是共享模式的節(jié)點,直接喚醒它
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
// 中斷當(dāng)前線程
selfInterrupt();
failed = false;
return;
}
}
// 到這了蠢箩,說明要么沒有排隊到當(dāng)前線程链蕊,要么CAS獲取鎖失敗,那就只有阻塞線程了
// shouldParkAfterFailedAcquire 如果線程應(yīng)阻塞谬泌,則返回true
// parkAndCheckInterrupt 阻塞當(dāng)前線程admol滔韵, 具體實現(xiàn)分析可以看上面lock的分析
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} finally {
// 線程被取消,摘掉節(jié)點
if (failed)
cancelAcquire(node);
}
}
獲取共享鎖總結(jié):
- 嘗試獲得鎖掌实,方法:
tryAcquireShared
- 獲得鎖成功奏属,直接返回;獲得鎖失敗潮峦,繼續(xù)執(zhí)行下面邏輯囱皿;
- 再次嘗試獲得鎖,方法:
doAcquireShared
- 新建排隊節(jié)點忱嘹,并加入到同步隊列嘱腥,方法:
addWaiter
,邏輯和獲得排它鎖的一致 - 自旋(嘗試獲得鎖拘悦,阻塞線程齿兔,等待被喚醒),直到成功獲得鎖
- 新建排隊節(jié)點忱嘹,并加入到同步隊列嘱腥,方法:
釋放共享鎖:public void unLock()
unlock方法調(diào)用的是sync.releaseShared(1)
础米,releaseShared也是AQS 方法中的方法分苇,不允許被繼承,表示將同步狀態(tài)設(shè)置回初始狀態(tài)屁桑,將鎖釋放医寿。
public final boolean releaseShared(int arg) {
// tryReleaseShared 我們自己實現(xiàn)的邏輯
if (tryReleaseShared(arg)) {
// 釋放鎖失敗,繼續(xù)釋放蘑斧,自旋直到釋放成功
doReleaseShared();
return true;
}
return false;
}
private void doReleaseShared() {
// 自旋
for (;;) {
Node h = head;
// 同步等待的隊列不為空
if (h != null && h != tail) {
int ws = h.waitStatus;
// 檢查狀態(tài)是否要喚醒下一個節(jié)點的線程
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
// 加入h節(jié)點是持有鎖的節(jié)點靖秩,會喚醒它的下一個節(jié)點線程
unparkSuccessor(h);
} else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
// 理論上喚醒一個就會退出
if (h == head) // loop if head changed
break;
}
}
總結(jié)
- AQS是Java中可以實現(xiàn)同步器功能的一個基礎(chǔ)框架须眷,我們自己也可以基于AQS實現(xiàn)想要的同步功能
- AQS 中用Node節(jié)點維護了一個雙向鏈表,用來保存排隊獲取鎖的線程沟突,已經(jīng)用來喚醒線程
- AQS 中為了一個
state
的同步狀態(tài)變量花颗,可以基于這個變量實現(xiàn)很多功能 - 實現(xiàn)AQS的幾個重要API,就可以實現(xiàn)一個簡單同步器的功能惠拭,其他像自旋扩劝,排隊,阻塞职辅,喚醒今野,AQS都已經(jīng)幫我們做好了
AQS 條件鎖分析請看這里:源碼分析:②ReentrantLock之條件鎖Condition