你了解Java中的同步器框架AQS嗎病往?

AbstractQueuedSynchronizer

public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements Serializable

概念

為實現(xiàn)依賴于先進先出 (FIFO) 等待隊列的阻塞鎖和相關(guān)同步器(信號量捣染、事件等等)提供一個框架。此類的設(shè)計目標(biāo)是成為依靠單個原子 int 值來表示狀態(tài)的大多數(shù)同步器的一個有用基礎(chǔ)停巷。子類必須定義更改此狀態(tài)的受保護方法液斜,并定義哪種狀態(tài)對于此對象意味著被獲取或被釋放。假定這些條件之后叠穆,此類中的其他方法就可以實現(xiàn)所有排隊和阻塞機制蕴潦。子類可以維護其他狀態(tài)字段匣缘,但只是為了獲得同步而只追蹤使用 getState()setState(int)compareAndSetState(int, int)方法來操作以原子方式更新的 int 值。

應(yīng)該將子類定義為非公共內(nèi)部幫助器類途茫,可用它們來實現(xiàn)其封閉類的同步屬性翼岁。類 AbstractQueuedSynchronizer 沒有實現(xiàn)任何同步接口。而是定義了諸如 acquireInterruptibly(int)之類的一些方法,在適當(dāng)?shù)臅r候可以通過具體的鎖和相關(guān)同步器來調(diào)用它們始鱼,以實現(xiàn)其公共方法。

此類支持默認(rèn)的獨占模式和共享模式之一脆贵,或者二者都支持医清。處于獨占模式下時,其他線程試圖獲取該鎖將無法取得成功卖氨。在共享模式下会烙,多個線程獲取某個鎖可能(但不是一定)會獲得成功。此類并不"了解"這些不同筒捺,除了機械地意識到當(dāng)在共享模式下成功獲取某一鎖時柏腻,下一個等待線程(如果存在)也必須確定自己是否可以成功獲取該鎖。處于不同模式下的等待線程可以共享相同的 FIFO 隊列系吭。通常五嫂,實現(xiàn)子類只支持其中一種模式,但兩種模式都可以在(例如)ReadWriteLock 中發(fā)揮作用肯尺。只支持獨占模式或者只支持共享模式的子類不必定義支持未使用模式的方法沃缘。

此類通過支持獨占模式的子類定義了一個嵌套的 AbstractQueuedSynchronizer.ConditionObject 類,可以將這個類用作 Condition實現(xiàn)则吟。其中:

  • isHeldExclusively()方法將報告同步對于當(dāng)前線程是否是獨占的
  • 使用當(dāng)前 getState() 值調(diào)用 release(int)方法則可以完全釋放此對象
  • 如果給定保存的狀態(tài)值槐臀,那么 acquire(int)方法可以將此對象最終恢復(fù)為它以前獲取的狀態(tài)

沒有別的 AbstractQueuedSynchronizer 方法創(chuàng)建這樣的條件,因此逾滥,如果無法滿足此約束峰档,則不要使用它败匹。AbstractQueuedSynchronizer.ConditionObject的行為當(dāng)然取決于其同步器實現(xiàn)的語義寨昙。

此類為內(nèi)部隊列提供了檢查、檢測和監(jiān)視方法掀亩,還為 condition 對象提供了類似方法舔哪。可以根據(jù)需要使用用于其同步機制的 AbstractQueuedSynchronizer 將這些方法導(dǎo)出到類中槽棍。

此類的序列化只存儲維護狀態(tài)的基礎(chǔ)原子整數(shù)捉蚤,因此已序列化的對象擁有空的線程隊列。需要可序列化的典型子類將定義一個 readObject 方法炼七,該方法在反序列化時將此對象恢復(fù)到某個已知初始狀態(tài)缆巧。

使用

為了將此類用作同步器的基礎(chǔ),需要適當(dāng)?shù)刂匦露x以下方法豌拙,這是通過使用 getState()陕悬、setState(int)compareAndSetState(int, int)方法來檢查和修改同步狀態(tài)來實現(xiàn)的:

  • tryAcquire(int)

    試圖在獨占模式下獲取對象狀態(tài)。此方法應(yīng)該查詢是否允許它在獨占模式下獲取對象狀態(tài)按傅,如果允許捉超,則獲取它胧卤。 此方法總是由執(zhí)行 acquire 的線程來調(diào)用。

    其定義了獲取鎖的一種方式拼岳,具體的邏輯判斷等枝誊,由acquire(int)方法調(diào)用并做調(diào)度,如下:

            @Override
            protected boolean tryAcquire(int arg) {
                Thread thread = Thread.currentThread();
                int c = getState();
                if (c == 0) {
                    if (!hasQueuedPredecessors() && compareAndSetState(c, arg)) {
                        setExclusiveOwnerThread(Thread.currentThread());
                    }
                    return true;
                } else if (getExclusiveOwnerThread() == thread) {
                    int nextC = c + arg;
                    if (nextC < 0) {
                        throw new Error("Maximum lock count exceeded");
                    }
                    setState(nextC);
                    return true;
                }
                return false;
            }
    
  • tryRelease(int)

    試圖設(shè)置狀態(tài)來反映獨占模式下的一個釋放惜纸。此方法總是由正在執(zhí)行釋放的線程調(diào)用叶撒。

    其定義了釋放鎖的一種方式,具體的邏輯判斷等堪簿,由release(int)方法調(diào)用并做調(diào)度痊乾,如下:

           @Override
            protected boolean tryRelease(int arg) {
                if (getExclusiveOwnerThread() != Thread.currentThread()) {
                    throw new IllegalMonitorStateException();
                }
    
                int c = getState() - arg;
                boolean free = false;
                if (c == 0) {
                    setExclusiveOwnerThread(null);
                    free = true;
                }
    
                setState(c);
                return free;
            }
    
  • tryAcquireShared(int)

    試圖在共享模式下獲取對象狀態(tài)。此方法應(yīng)該查詢是否允許它在共享模式下獲取對象狀態(tài)椭更,如果允許哪审,則獲取它。 此方法總是由執(zhí)行 acquire 線程來調(diào)用虑瀑。如果此方法報告失敗湿滓,則 acquire 方法可以將線程加入隊列(如果還沒有將它加入隊列),直到其獲得了其他某個線程釋放了該線程的信號舌狗。

  • tryReleaseShared(int)

    試圖設(shè)置狀態(tài)來反映共享模式下的一個釋放叽奥。 此方法總是由正在執(zhí)行釋放的線程調(diào)用。

  • isHeldExclusively()

    如果對于當(dāng)前(正調(diào)用的)線程痛侍,同步是以獨占方式進行的朝氓,則返回 true。

            @Override
            protected boolean isHeldExclusively() {
                return getExclusiveOwnerThread() == Thread.currentThread();
            }
    
  • AbstractOwnableSynchronizer.setExclusiveOwnerThread(Thread)

    設(shè)置當(dāng)前擁有獨占訪問的線程主届。

默認(rèn)情況下,每個方法都拋出UnsupportedOperationException赵哲。這些方法的實現(xiàn)在內(nèi)部必須是線程安全的,通常應(yīng)該很短并且不被阻塞君丁。定義這些方法是使用此類的唯一受支持的方式枫夺。其他所有方法都被聲明final,因為它們無法是各不相同的绘闷。

您也可以查找從 AbstractOwnableSynchronizer 繼承的方法橡庞,用于跟蹤擁有獨占同步器的線程。鼓勵使用這些方法印蔗,這允許監(jiān)控和診斷工具來幫助用戶確定哪個線程保持鎖扒最。

即使此類基于內(nèi)部的某個 FIFO 隊列,它也無法強行實施 FIFO 獲取策略华嘹。獨占同步的核心采用以下形式:

 Acquire:
     while (!tryAcquire(arg)) {
        enqueue thread if it is not already queued;
        possibly block current thread;
     }

 Release:
     if (tryRelease(arg))
        unblock the first queued thread;
 

因為要在加入隊列之前檢查線程的獲取狀況吧趣,所以新獲取的線程可能闖入其他被阻塞的和已加入隊列的線程之前。不過如果需要,可以內(nèi)部調(diào)用一個或多個檢查方法再菊,通過定義 tryAcquiretryAcquireShared 來禁用闖入爪喘。特別是 getFirstQueuedThread() 沒有返回當(dāng)前線程的時候,嚴(yán)格的 FIFO 鎖定可以定義 tryAcquire 立即返回 false纠拔。只有 hasQueuedThreads()返回 true 并且 getFirstQueuedThread 不是當(dāng)前線程時秉剑,更好的非嚴(yán)格公平的版本才可能會立即返回 false;如果 getFirstQueuedThread 不為 null 并且不是當(dāng)前線程稠诲,則產(chǎn)生的結(jié)果相同侦鹏。出現(xiàn)進一步的變體也是有可能的。

對于默認(rèn)闖入(也稱為 greedy臀叙、renouncementconvoy-avoidance)策略略水,吞吐量和可伸縮性通常是最高的。盡管無法保證這是公平的或是無偏向的劝萤,但允許更早加入隊列的線程先于更遲加入隊列的線程再次爭用資源渊涝,并且相對于傳入的線程,每個參與再爭用的線程都有平等的成功機會床嫌。此外跨释,盡管從一般意義上說,獲取并非“自旋”厌处,它們可以在阻塞之前對用其他計算所使用的 tryAcquire 執(zhí)行多次調(diào)用鳖谈。在只保持獨占同步時,這為自旋提供了最大的好處阔涉,但不是這種情況時缆娃,也不會帶來最大的負(fù)擔(dān)。如果需要這樣做瑰排,那么可以使用“快速路徑”檢查來先行調(diào)用 acquire 方法贯要,以這種方式擴充這一點,如果可能不需要爭用同步器凶伙,則只能通過預(yù)先檢查 hasContended()來確認(rèn)這一點郭毕。

通過特殊化其同步器的使用范圍它碎,此類為部分同步化提供了一個有效且可伸縮的基礎(chǔ)函荣,同步器可以依賴于 int 型的 state、acquire 和 release 參數(shù)扳肛,以及一個內(nèi)部的 FIFO 等待隊列傻挂。這些還不夠的時候,可以使用 atomic類挖息、自己的定制 Queue 類和 LockSupport阻塞支持金拒,從更低級別構(gòu)建同步器。

實現(xiàn)公平可重入鎖

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;

/**
 * 自定義可重入公平鎖.
 */
public class CustomFairLock {

    private static final class Sync extends AbstractQueuedSynchronizer {

        /**
         * tryAcquire定義了獲取鎖的方式
         *
         * @param arg
         * @return
         */
        @Override
        protected boolean tryAcquire(int arg) {
            Thread thread = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors()
                        && compareAndSetState(0, arg)) {
                    setExclusiveOwnerThread(Thread.currentThread());
                }
                return true;
            } else if (getExclusiveOwnerThread() == thread) {
                int nextC = c + arg;
                if (nextC < 0) {
                    throw new Error("Maximum lock count exceeded");
                }
                setState(nextC);
                return true;
            }
            return false;
        }

        /**
         * 定義了釋放鎖的方式
         *
         * @param arg
         * @return
         */
        @Override
        protected boolean tryRelease(int arg) {
            int c = getState() - arg;
            if (getExclusiveOwnerThread() != Thread.currentThread()) {
                throw new IllegalMonitorStateException();
            }
            boolean free = false;
            if (c == 0) {
                setExclusiveOwnerThread(null);
                free = true;
            }

            setState(c);
            return free;
        }

        @Override
        protected boolean isHeldExclusively() {
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }
    }

    private Sync sync = new Sync();

    public void lock() {
        sync.acquire(1);

    }

    public void tryLock(long timeout, TimeUnit unit) throws InterruptedException {
        sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

    public boolean unlock() {
        return sync.release(1);
    }
}

ReentrantLock

對于ReentrantLock其實現(xiàn)公平鎖和非公平鎖是通過以下代碼來區(qū)分的:

非公平鎖tryAcquire():

final Thread current = Thread.currentThread();           
int c = getState();                                      
if (c == 0) {                                            
    if (compareAndSetState(0, acquires)) {               
        setExclusiveOwnerThread(current);                
        return true;                                     
    }                                                    
}                                                        
else if (current == getExclusiveOwnerThread()) {         
    int nextc = c + acquires;                            
    if (nextc < 0) // overflow                           
        throw new Error("Maximum lock count exceeded");  
    setState(nextc);                                     
    return true;                                         
}                                                        
return false;                                            

公平鎖tryAcquire():

final Thread current = Thread.currentThread();                 
int c = getState();                                            
if (c == 0) { 
  //唯一的區(qū)別在這里,公平鎖優(yōu)先判斷隊列中有無以等待的線程,而非公平的則不去判斷,直接compareAndSetState狀態(tài)
    if (!hasQueuedPredecessors() &&                            
        compareAndSetState(0, acquires)) {                     
        setExclusiveOwnerThread(current);                      
        return true;                                           
    }                                                          
}                                                              
else if (current == getExclusiveOwnerThread()) {               
    int nextc = c + acquires;                                  
    if (nextc < 0)                                             
        throw new Error("Maximum lock count exceeded");        
    setState(nextc);                                           
    return true;                                               
}                                                              
return false;                                                                                        
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市绪抛,隨后出現(xiàn)的幾起案子资铡,更是在濱河造成了極大的恐慌,老刑警劉巖幢码,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笤休,死亡現(xiàn)場離奇詭異,居然都是意外死亡症副,警方通過查閱死者的電腦和手機店雅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贞铣,“玉大人闹啦,你說我怎么就攤上這事≡樱” “怎么了窍奋?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長酱畅。 經(jīng)常有香客問我费变,道長,這世上最難降的妖魔是什么圣贸? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任挚歧,我火速辦了婚禮,結(jié)果婚禮上吁峻,老公的妹妹穿的比我還像新娘滑负。我一直安慰自己,他們只是感情好用含,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布矮慕。 她就那樣靜靜地躺著,像睡著了一般啄骇。 火紅的嫁衣襯著肌膚如雪痴鳄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天缸夹,我揣著相機與錄音痪寻,去河邊找鬼。 笑死虽惭,一個胖子當(dāng)著我的面吹牛橡类,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播芽唇,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼顾画,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起研侣,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤谱邪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后庶诡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體虾标,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年灌砖,在試婚紗的時候發(fā)現(xiàn)自己被綠了璧函。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡基显,死狀恐怖蘸吓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情撩幽,我是刑警寧澤库继,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站窜醉,受9級特大地震影響宪萄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜榨惰,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一拜英、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧琅催,春花似錦居凶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至缠黍,卻和暖如春弄兜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瓷式。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工替饿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蒿往。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓盛垦,卻偏偏與公主長得像湿弦,于是被迫代替她去往敵國和親瓤漏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容