顯式鎖(二)

之前在介紹線程的狀態(tài)及轉(zhuǎn)換中旺芽,提到了Object有wait()罐柳、wait(long timeout)祟同、notify()、notifyAll()方法厢蒜,這些方法與synchronized配合使用霞势,可以實(shí)現(xiàn)等待/通知模式烹植。無(wú)獨(dú)有偶,Conditon接口也提供了類似Object的監(jiān)視器方法愕贡,與Lock配合也能實(shí)現(xiàn)通知/等待模式草雕。

1、Condition接口

①接口介紹

在上篇顯式鎖(一)介紹Lock接口時(shí)固以,Lock有一個(gè)方法叫" Condition newCondition(); "墩虹,此方法是獲取等待通知組件,也就是Conditon憨琳,獲取的Condition與此Lock綁定诫钓。也可以說(shuō),Conditon是依賴Lock對(duì)象篙螟。

java-8
public interface Condition {
    //當(dāng)前線程進(jìn)入等待狀態(tài)直到被通知或中斷
    void await() throws InterruptedException;
    //當(dāng)前線程進(jìn)入等待狀態(tài)直到被通知菌湃,對(duì)中斷不敏感
    void awaitUninterruptibly();
    //當(dāng)前線程進(jìn)入等待狀態(tài)直到被通知、中斷或超時(shí)
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    //當(dāng)前線程進(jìn)入等待狀態(tài)直到被通知遍略、中斷惧所,在指定時(shí)間內(nèi)被通知返回true,否則false
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    boolean awaitUntil(Date deadline) throws InterruptedException;
    //喚醒一個(gè)等待在Condition上的線程
    void signal();
    //喚醒所有等待在Conditon上的線程
    void signalAll();
}

一般的使用套路如下:

Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();

public void conditionWait() throws InterruptedException{
      lock.lock();
      try{
           condition.await();
      }finally{
           lock.unlock();
      }
}

public void conditionSingal() throws InterruptedException{
       lock.lock();
       try{
            condition.singal();
       }finally{
            lock.unlock();
       }  
}
②使用示例

通過(guò)一個(gè)生產(chǎn)者/消費(fèi)者的例子來(lái)使用下Condition:

/**
 * Created by bxw on 2017/10/5.
 */
public class Depot {
    private int capacity;
    private int size;
    private Lock lock;
    private Condition consumerCond;
    private Condition produceCond;

    public Depot(int capacity) {
        this.capacity = capacity;
        this.size = 0;
        this.lock = new ReentrantLock();
        this.consumerCond = lock.newCondition();
        this.produceCond = lock.newCondition();
    }

    public void produce(int val){
        lock.lock();
        try {
            int left = val;
            while(left > 0){
                while(size >= capacity){
                    produceCond.await();
                }
                int produce = (left + size) > capacity ? (capacity - size) : left;
                size += produce;
                left -= produce;
                System.out.println(Thread.currentThread().getName() + ", ProcudeVal=" + val +", produce=" + produce + ",size=" + size);
                consumerCond.signalAll();
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void consumer(int val){
        lock.lock();
        try {
            int left = val;
            while (left > 0) {
                while (size <= 0) {
                    consumerCond.await();
                }
                int consumer = (size <= left) ? size : left;
                size -= consumer;
                left -= consumer;
                System.out.println(Thread.currentThread().getName() + ", ConsumerVal=" + val + ", consumer=" + consumer + ", size=" + size);
                produceCond.signalAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

public class Producer {
    private Depot depot;
    public Producer(Depot depot) {
        this.depot = depot;
    }

    public void produceThing(final int amount) {
        new Thread(new Runnable() {
            public void run() {
                depot.produce(amount);
            }
        }).start();
    }
}

public class Consumer {
    private Depot depot;
    public Consumer(Depot depot) {
        this.depot = depot;
    }

    public void consumerThing(final int amount) {
        new Thread(new Runnable() {
            public void run() {
                depot.consumer(amount);
            }
        }).start();
    }
}

public class Test {
    public static void main(String[] args) {
        // 倉(cāng)庫(kù)
        Depot depot = new Depot(100);
        // 消費(fèi)者
        Consumer consumer = new Consumer(depot);
        // 生產(chǎn)者
        Producer produce = new Producer(depot);
        produce.produceThing(5);
        consumer.consumerThing(5);
        produce.produceThing(2);
        consumer.consumerThing(5);
        produce.produceThing(5);
    }
}

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

從結(jié)果看出,Thread-0和Thread-1比較和諧绪杏,Thread-0生產(chǎn)了5個(gè)纯路,Thread-1消費(fèi)了5個(gè),剩余0寞忿。接著Thread-2又生產(chǎn)了2個(gè)驰唬,Thread-3需要消費(fèi)5個(gè),但倉(cāng)庫(kù)中只有2個(gè)腔彰,所以先將庫(kù)存的2個(gè)產(chǎn)品全部消費(fèi)叫编,然后這個(gè)線程進(jìn)入等待隊(duì)列,等待生產(chǎn)霹抛,隨后生產(chǎn)出了5個(gè)產(chǎn)品搓逾,生產(chǎn)者生產(chǎn)后又執(zhí)行signalAll方法將等待隊(duì)列中所有的線程都喚醒,Thread-3繼續(xù)消費(fèi)還需要的3個(gè)產(chǎn)品杯拐,倉(cāng)庫(kù)還剩余了2個(gè)霞篡。

③實(shí)現(xiàn)分析

首先從Lock.newCondition分析,以ReentrantLock為例端逼,會(huì)返回一個(gè)ConditionObject對(duì)象朗兵。


返回ConditionObject對(duì)象

那ConditionObject為何物,原來(lái)它是AbstractQueuedSynchronizer(簡(jiǎn)稱AQS)的一個(gè)內(nèi)部類顶滩,擁有兩個(gè)節(jié)點(diǎn):頭節(jié)點(diǎn)余掖、尾節(jié)點(diǎn)。

ConditionObject

因?yàn)槊總€(gè)ConditionObject都包含一個(gè)等待隊(duì)列礁鲁,這是Conditon實(shí)現(xiàn)等待/通知的關(guān)鍵盐欺。等待隊(duì)列是個(gè)FIFO隊(duì)列赁豆,隊(duì)列中每個(gè)節(jié)點(diǎn)都包含了一個(gè)線程的引用,該線程是在Condition對(duì)象上等待的線程冗美,如果一個(gè)線程調(diào)用了Condition.await()方法魔种,該線程就釋放鎖、構(gòu)造節(jié)點(diǎn)加入等待隊(duì)列并進(jìn)入等待狀態(tài)粉洼。節(jié)點(diǎn)(Node)的定義也是AQS中的一個(gè)內(nèi)部類务嫡。

Node
等待隊(duì)列的基本結(jié)構(gòu)

Condition擁有首節(jié)點(diǎn)的引用,新增節(jié)點(diǎn)只需要把原來(lái)的尾節(jié)點(diǎn)nextWaiter指向它漆改,并且更新尾節(jié)點(diǎn)即可心铃。
在Object的監(jiān)視器模型上,一個(gè)對(duì)象擁有一個(gè)同步隊(duì)列和等待隊(duì)列挫剑,而Lock(同步器)擁有一個(gè)同步隊(duì)列和多個(gè)等待隊(duì)列關(guān)系圖如下:

同步隊(duì)列與等待隊(duì)列

從隊(duì)列角度看去扣,調(diào)用Condition的await()方法,就是把同步隊(duì)列的首節(jié)點(diǎn)移動(dòng)到了Condition的等待隊(duì)列樊破,線程狀態(tài)變?yōu)榈却隣顟B(tài)愉棱。

總結(jié)

此篇主要介紹了依賴Lock的Condition,Condition的等待/通知模型和Object的等待/通知模型有些相似哲戚,兩者可以對(duì)比著使用奔滑,分析了Condition的簡(jiǎn)單實(shí)現(xiàn)原理,引出了隊(duì)列同步器的概念顺少,下篇就簡(jiǎn)單介紹下AQS朋其。

參考書(shū)籍《并發(fā)編程的藝術(shù)》,
案例參考博客 http://www.cnblogs.com/zhengbin/p/6420984.html

略陳固陋脆炎,如有不當(dāng)之處梅猿,歡迎各位看官批評(píng)指正!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末秒裕,一起剝皮案震驚了整個(gè)濱河市袱蚓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌几蜻,老刑警劉巖喇潘,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異梭稚,居然都是意外死亡颖低,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門哨毁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)枫甲,“玉大人,你說(shuō)我怎么就攤上這事扼褪∠牖茫” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵话浇,是天一觀的道長(zhǎng)脏毯。 經(jīng)常有香客問(wèn)我,道長(zhǎng)幔崖,這世上最難降的妖魔是什么食店? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮赏寇,結(jié)果婚禮上吉嫩,老公的妹妹穿的比我還像新娘。我一直安慰自己嗅定,他們只是感情好自娩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著渠退,像睡著了一般忙迁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上碎乃,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天姊扔,我揣著相機(jī)與錄音,去河邊找鬼梅誓。 笑死恰梢,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的梗掰。 我是一名探鬼主播删豺,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼愧怜!你這毒婦竟也來(lái)了呀页?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤拥坛,失蹤者是張志新(化名)和其女友劉穎蓬蝶,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體猜惋,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丸氛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了著摔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缓窜。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出禾锤,到底是詐尸還是另有隱情私股,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布恩掷,位于F島的核電站倡鲸,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏黄娘。R本人自食惡果不足惜峭状,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望逼争。 院中可真熱鬧优床,春花似錦、人聲如沸誓焦。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)罩阵。三九已至竿秆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間稿壁,已是汗流浹背幽钢。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留傅是,地道東北人匪燕。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像喧笔,于是被迫代替她去往敵國(guó)和親帽驯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

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