Semaphore

Semaphore

Semaphore 是 Java 并發(fā)包中提供的一個工具類早直,翻譯過來為“信號量”寥假,作用是控制并發(fā)線程的數(shù)量。

類的結(jié)構(gòu)

先來看一下 Semaphore 的結(jié)構(gòu):

Semaphore-Outline

Semaphore 中有三個個內(nèi)部類:

  • Sync 繼承了 AbstractQueuedSynchronizer霞扬,重寫了 tryReleaseShared 方法糕韧,還有一些在 Semaphore 中用到的輔助方法,都是對線程進(jìn)行控制的
  • NonfairSync 繼承了 Sync喻圃,通過重寫 tryAcquireShared 方法實(shí)現(xiàn)了非公平的線程競爭機(jī)制萤彩,這個方法內(nèi)部是調(diào)用了 SyncnonfairTryAcquireShared 方法
  • FairSync 繼承了 Sync,也是通過重寫 tryAcquireShared 方法實(shí)現(xiàn)了公平的線程競爭機(jī)制

由此可以看出斧拍,Semaphore 實(shí)際上是通過 AQS 的共享鎖來控制線程的雀扶。

構(gòu)造方法

Semaphore 的構(gòu)造函數(shù)對變量 sync 進(jìn)行了初始化,默認(rèn)是非公平競爭的肆汹,也可以通過指定參數(shù)設(shè)置為公平競爭愚墓,其他所有的方法內(nèi)部都是調(diào)用了 sync 變量的方法。

public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}

public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

acquire

acquire 用來獲取信號量昂勉,Semaphore 提供了重載方法浪册,也可以獲取多個信號量。

public void acquire() throws InterruptedException { // 獲取 1 個信號量
    sync.acquireSharedInterruptibly(1);
}
    
public void acquire(int permits) throws InterruptedException {
    if (permits < 0) throw new IllegalArgumentException(); // 參數(shù)判斷
    sync.acquireSharedInterruptibly(permits); // 獲取指定個數(shù)的信號量
}

acquire 方法內(nèi)部調(diào)用了 syncacquireSharedInterruptibly 方法岗照,這里并沒有對這個方法進(jìn)行重寫村象,所以調(diào)用的還是 AbstractQueuedSynchronizer 中的方法,這里就不貼代碼了攒至,但是 acquireSharedInterruptibly 方法內(nèi)部又調(diào)用了 tryAcquireShared 方法煞肾,由于 Semaphore 類提供了公平和非公平兩種競爭機(jī)制,所以 tryAcquireShared 也有兩種不同的實(shí)現(xiàn)嗓袱。

來看一下兩種獲取鎖的方法籍救。

tryAcquireShared

先來看一下非公平的 tryAcquireShared,這個方法是內(nèi)部類 NonfairSync 中的:

protected int tryAcquireShared(int acquires) {
    return nonfairTryAcquireShared(acquires);
}

繼續(xù)看 nonfairTryAcquireShared渠抹,這個方法是 Sync 提供的:

final int nonfairTryAcquireShared(int acquires) {
    for (;;) { // 循環(huán)直到獲取成功
        int available = getState(); // 獲取當(dāng)前 state蝙昙,在這里就是信號量
        int remaining = available - acquires; // 減去獲取的信號量后剩余的信號量
        // 如果信號量小于 0(獲取失斏撂选) 或者更新信號量成功,返回剩余信號量
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}

再看公平的 tryAcquireShared奇颠,這個方法是內(nèi)部類 FairSync 中的:

protected int tryAcquireShared(int acquires) {
    for (;;) {
        // 先判斷在當(dāng)前線程之前是否有線程正在 acquire败去,如果有返回 -1 表示獲取失敗
        if (hasQueuedPredecessors())
            return -1;
        int available = getState();
        int remaining = available - acquires;
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}

相對于非公平的 nonfairTryAcquireShared,公平的 tryAcquireShared 先判斷在當(dāng)前線程之前是否有線程正在 acquire烈拒,如果有就直接返回 -1 表示 tryAcquire 失敗了圆裕,這就是公平的體現(xiàn)。

acquire 還提供了忽略中斷的 acquireUninterruptibly荆几,這里就不展開來說了吓妆。

release

release 方法用來釋放信號量,同樣的吨铸,Semaphore 提供了重載方法行拢,可以釋放多個信號量。

public void release() {
    sync.releaseShared(1);
}

public void release(int permits) {
    if (permits < 0) throw new IllegalArgumentException();
    sync.releaseShared(permits);
}

acquire 一樣诞吱,release 方法調(diào)用了 AbstractQueuedSynchronizer 中的 releaseShared 方法舟奠,releaseShared 方法內(nèi)部又調(diào)用了 tryReleaseShared 方法,這個方法由子類 Sync 重寫:

protected final boolean tryReleaseShared(int releases) {
    for (;;) {
        int current = getState();
        int next = current + releases;
        if (next < current) // overflow 溢出
            throw new Error("Maximum permit count exceeded");
        if (compareAndSetState(current, next))
            return true;
    }
}

tryReleaseShared 的邏輯比較簡單房维,將信號量歸還沼瘫,CAS 更新 state 即可。

Semaphore 還提供了 tryAcquire 方法以及一些輔助方法咙俩,這里不再贅述晕鹊。

總結(jié)

Semaphore 提供了線程的控制方案,對線程的競爭提供了公平和非公平的方式暴浦。

應(yīng)用

import java.util.concurrent.Semaphore;

public class SemaphoreTest {

    private static final int numOfThreads = 5; // 線程數(shù)

    private static final int sleepTime = 3000; // 睡眠時(shí)間

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(numOfThreads);
        System.out.println("停車場一共有 " + numOfThreads + " 個停車位");
        for (int i = 0; i < 2 * numOfThreads; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        semaphore.acquire();
                        System.out.println(Thread.currentThread().getName() + " 停車");
                        Thread.sleep(sleepTime);
                        System.out.println(Thread.currentThread().getName() + " 開走了");
                        semaphore.release();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }

}

輸出結(jié)果為:

停車場一共有 5 個停車位
Thread-2 停車
Thread-0 停車
Thread-1 停車
Thread-4 停車
Thread-3 停車
Thread-0 開走了
Thread-2 開走了
Thread-8 停車
Thread-7 停車
Thread-1 開走了
Thread-4 開走了
Thread-3 開走了
Thread-9 停車
Thread-6 停車
Thread-5 停車
Thread-7 開走了
Thread-8 開走了
Thread-9 開走了
Thread-6 開走了
Thread-5 開走了

CountDownLatch溅话、CyclicBarrier 和 Semaphore

  • CountDownLatch 由一類線程控制另一類線程,CyclicBarrier 是一類線程都執(zhí)行到了 await 方法后再繼續(xù)執(zhí)行歌焦,Semaphore 則是控制同時(shí)執(zhí)行的線程的數(shù)量
  • CountDownLatch 主要通過 await 方法和 countDown 方法控制飞几,CyclicBarrier 只通過 await 方法,Semaphore 通過 acquire 方法和 release 方法
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子煮寡,更是在濱河造成了極大的恐慌顽素,老刑警劉巖茫虽,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)以躯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人忧设,你說我怎么就攤上這事刁标。” “怎么了址晕?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵膀懈,是天一觀的道長。 經(jīng)常有香客問我谨垃,道長启搂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任刘陶,我火速辦了婚禮胳赌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘易核。我一直安慰自己匈织,他們只是感情好浪默,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布牡直。 她就那樣靜靜地躺著,像睡著了一般纳决。 火紅的嫁衣襯著肌膚如雪碰逸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天阔加,我揣著相機(jī)與錄音饵史,去河邊找鬼。 笑死胜榔,一個胖子當(dāng)著我的面吹牛胳喷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播夭织,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼吭露,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了尊惰?” 一聲冷哼從身側(cè)響起讲竿,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎弄屡,沒想到半個月后题禀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡膀捷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年迈嘹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片全庸。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡江锨,死狀恐怖吃警,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情啄育,我是刑警寧澤酌心,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站挑豌,受9級特大地震影響安券,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜氓英,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一侯勉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧铝阐,春花似錦址貌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至吹害,卻和暖如春螟凭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背它呀。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工螺男, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人纵穿。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓下隧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親谓媒。 傳聞我的和親對象是個殘疾皇子淆院,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

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