Java 并發(fā)專題:Semaphore 實(shí)現(xiàn) 互斥 與 連接池

Semaphore,位于java.util.concurrent包下面

  • Semaphore中管理著一組虛擬的許可,許可的初始數(shù)量可通過構(gòu)造函數(shù)來指定new Semaphore(1);跨蟹,執(zhí)行操作時(shí)可以首先獲得許可semaphore.acquire();熙尉,并在使用后釋放許可semaphore.release();。如果沒有許可车柠,那么acquire方法將會一直阻塞直到有許可(或者直到被終端或者操作超時(shí))剔氏。
  • Semaphore是一種在多線程環(huán)境下使用的設(shè)施,該設(shè)施負(fù)責(zé)協(xié)調(diào)各個(gè)線程竹祷,以保證它們能夠正確谈跛、合理的使用公共資源的設(shè)施,也是操作系統(tǒng)中用于控制進(jìn)程同步互斥的量塑陵。
  • Semaphore分為單值和多值兩種感憾,前者只能被一個(gè)線程獲得,后者可以被若干個(gè)線程獲得令花。
    作用:可以用來控制同時(shí)訪問某個(gè)特定資源的操作數(shù)量阻桅,或者某個(gè)操作的數(shù)量。

下面使用Semaphore實(shí)現(xiàn)兩個(gè)例子:

  1. 互斥
    大家都學(xué)過操作系統(tǒng)兼都,都知道互斥的概念嫂沉,比較簡單的互斥實(shí)現(xiàn),比如PV操作扮碧,判斷資源趟章,然后忙等實(shí)現(xiàn)互斥杏糙;上一篇也說過,忙等對CPU的消耗巨大蚓土,下面我們通過Semaphore來實(shí)現(xiàn)一個(gè)比較好的互斥操作:
    假設(shè)我們公司只有一臺打印機(jī)宏侍,我們需要對這臺打印機(jī)的打印操作進(jìn)行互斥控制:
    首先是沒有使用Semaphore來實(shí)現(xiàn)互斥操作
public class Test {
    public void print(String str) throws InterruptedException {
        System.out.println(Thread.currentThread().getName()+"準(zhǔn)備完成,開始打印...");
        Thread.sleep(1000);
        System.out.println(Thread.currentThread().getName()+"正在打印..."+str);
        System.out.println(Thread.currentThread().getName()+"打印完成蜀漆,退出程序 ...");
    }

    public static void main(String[] args) {
        Test t = new Test();
        
        for(int i = 0; i < 10 ; i++) {
            new Thread() {
                public void run() {
                    try {
                        t.print("hello");
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    }
}

運(yùn)行結(jié)果:

沒有Semaphore

從結(jié)果可以看出谅河,一個(gè)Thread還沒有打印完成,退出程序确丢,別的Thread就進(jìn)入開始打印了绷耍,這樣必然沒有按照正確的流程來打印。

使用Semaphore來控制互斥訪問數(shù)

import java.util.concurrent.Semaphore;
/**
 * 使用Semphore實(shí)現(xiàn)互斥訪問打印機(jī)
 * @author ghw
 *
 */
public class Test {
    //定義初始值為1的信號量 
    private Semaphore semaphore = new Semaphore(1);
    //模擬打印機(jī)打印操作
    public void print(String str) throws InterruptedException {
        //請求許可
        semaphore.acquire();
        System.out.println(Thread.currentThread().getName()+"準(zhǔn)備完成蠕嫁,開始打印...");
        //模擬打印時(shí)間
        Thread.sleep(1000);
        System.out.println(Thread.currentThread().getName()+"正在打印..."+str);
        System.out.println(Thread.currentThread().getName()+"打印完成锨天,退出程序 ...");
        //釋放許可
        semaphore.release();
    }

    public static void main(String[] args) {
        Test t = new Test();
        //開啟10個(gè)線程執(zhí)行打印任務(wù)
        for(int i = 0; i < 10 ; i++) {
            new Thread() {
                public void run() {
                    try {
                        t.print("hello");
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    }
}

運(yùn)行結(jié)果:

使用Semphore

從結(jié)果可以看出,多個(gè)線程雖然是無序執(zhí)行的剃毒,但每個(gè)線程執(zhí)行完成后退出程序才開始執(zhí)行下一個(gè)線程病袄,這樣就不會亂。

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;

public class ConnectPool {
    
    private final List<Conn> pool = new ArrayList<Conn>(3);  
    private Semaphore semaphore = new Semaphore(3);

    
    public ConnectPool() {
        pool.add(new Conn());
        pool.add(new Conn());
        pool.add(new Conn());
    }
    
    public Conn getConn() throws InterruptedException {
        semaphore.acquire();  
        Conn c = null  ;  
        synchronized (pool)  
        {  
            c = pool.remove(0);  
        }  
        System.out.println(Thread.currentThread().getName()+" 獲得一個(gè)連接 " + c);  
        return c ;  
    }
    
    public void release(Conn c) {
        pool.add(c);  
        System.out.println(Thread.currentThread().getName()+" 釋放了一個(gè)連接 " + c);  
        semaphore.release();  
    }
    
    public static void main(String[] args) {
        ConnectPool pool = new ConnectPool();
        
        new Thread() {
            public void run() {
                try {
                    Conn c = pool.getConn();
                    Thread.sleep(3000);
                    pool.release(c);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
        }.start();
        
        for(int i=0;i<5;i++) {
             new Thread() {
                public void run() {
                    try {
                        Conn c = pool.getConn();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                };
                
             }.start();
        }
    }
}

運(yùn)行結(jié)果:

運(yùn)行結(jié)果3

先讓Thread-0持有一個(gè)連接3秒赘阀,然后瞬間讓3個(gè)線程再去請求分配連接益缠,造成Thread-3一直等到Thread-0對連接的釋放,然后獲得連接基公。

通過兩個(gè)例子幅慌,基本已經(jīng)了解了Semaphore的用法,這里的線程池例子只是為了說明Semaphore的用法轰豆,真實(shí)的實(shí)現(xiàn)代碼比這復(fù)雜的多胰伍,而且可能也不會直接用Semaphore。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末酸休,一起剝皮案震驚了整個(gè)濱河市骂租,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌斑司,老刑警劉巖渗饮,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異宿刮,居然都是意外死亡互站,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進(jìn)店門僵缺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胡桃,“玉大人,你說我怎么就攤上這事磕潮〈湟龋” “怎么了懊纳?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長亡容。 經(jīng)常有香客問我,道長冤今,這世上最難降的妖魔是什么闺兢? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮戏罢,結(jié)果婚禮上屋谭,老公的妹妹穿的比我還像新娘。我一直安慰自己龟糕,他們只是感情好桐磁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著讲岁,像睡著了一般我擂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上缓艳,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天校摩,我揣著相機(jī)與錄音,去河邊找鬼阶淘。 笑死衙吩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的溪窒。 我是一名探鬼主播坤塞,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼澈蚌!你這毒婦竟也來了摹芙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤惜浅,失蹤者是張志新(化名)和其女友劉穎瘫辩,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體坛悉,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伐厌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了裸影。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挣轨。...
    茶點(diǎn)故事閱讀 39,932評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖轩猩,靈堂內(nèi)的尸體忽然破棺而出卷扮,到底是詐尸還是另有隱情荡澎,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布晤锹,位于F島的核電站摩幔,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏鞭铆。R本人自食惡果不足惜或衡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望车遂。 院中可真熱鬧封断,春花似錦、人聲如沸舶担。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽衣陶。三九已至柄瑰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間剪况,已是汗流浹背狱意。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拯欧,地道東北人详囤。 一個(gè)月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像镐作,于是被迫代替她去往敵國和親藏姐。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評論 2 354

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

  • Java SE 基礎(chǔ): 封裝该贾、繼承羔杨、多態(tài) 封裝: 概念:就是把對象的屬性和操作(或服務(wù))結(jié)合為一個(gè)獨(dú)立的整體,并盡...
    Jayden_Cao閱讀 2,109評論 0 8
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理杨蛋,服務(wù)發(fā)現(xiàn)兜材,斷路器,智...
    卡卡羅2017閱讀 134,656評論 18 139
  • 一逞力、多線程 說明下線程的狀態(tài) java中的線程一共有 5 種狀態(tài)曙寡。 NEW:這種情況指的是,通過 New 關(guān)鍵字創(chuàng)...
    Java旅行者閱讀 4,680評論 0 44
  • 本文章轉(zhuǎn)載于搜狗測試 一寇荧、什么是API接口測試举庶? API接口有多種,個(gè)人將其劃分為三類。 第一種是函數(shù)級別的黍图,測試...
    夜境閱讀 1,376評論 1 2
  • 有些人一直在你的記憶當(dāng)中泌射,不是他對你有多么的重要庵寞,也不是你永遠(yuǎn)都放不下他,只是單純的因?yàn)樗骄冢隳阕哌^的那一段路酷含,是...
    婉彤Sharon閱讀 578評論 0 0