Java基礎(chǔ)建設(shè) 1-notify/wait方法

一豫尽、使用用例

public class ThreadTest {
    static final Object obj = new Object();

    private static boolean flag = false;

    public static void main(String[] args) throws Exception {

        Thread consume = new Thread(new Consume(), "Consume");
        Thread produce = new Thread(new Produce(), "Produce");
        consume.start();
        Thread.sleep(1000);
        produce.start();

        try {
            produce.join();//強(qiáng)制生產(chǎn)者先退出
            consume.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 生產(chǎn)者線程
    static class Produce implements Runnable {

        @Override
        public void run() {

            synchronized (obj) {
                System.out.println("進(jìn)入生產(chǎn)者線程");
                System.out.println("生產(chǎn)");
                try {
                    TimeUnit.MILLISECONDS.sleep(2000);  //模擬生產(chǎn)過程
                    flag = true;
                    obj.notify();  //通知消費(fèi)者
                    System.out.println("通知消費(fèi)者?");
                    TimeUnit.MILLISECONDS.sleep(1000);  //模擬其他耗時操作
                    System.out.println("退出生產(chǎn)者線程");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //消費(fèi)者線程
    static class Consume implements Runnable {

        @Override
        public void run() {
            System.out.println("進(jìn)入消費(fèi)者線程");
            System.out.println("wait flag 1:" + flag);
            synchronized (obj) {
                while (!flag) {  //判斷條件是否滿足,若不滿足則等待
                    try {
                        System.out.println("還沒生產(chǎn)卢鹦,進(jìn)入等待");

                        obj.wait();

                        System.out.println("結(jié)束等待");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            System.out.println("wait flag 2:" + flag);
            System.out.println("消費(fèi)");
            System.out.println("退出消費(fèi)者線程");
        }
    }
}

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

進(jìn)入消費(fèi)者線程
wait flag 1:false
還沒生產(chǎn),進(jìn)入等待
進(jìn)入生產(chǎn)者線程
生產(chǎn)
通知消費(fèi)者劝堪?
退出生產(chǎn)者線程
結(jié)束等待
wait flag 2:true
消費(fèi)
退出消費(fèi)者線程

二冀自、原理
問題1:為什么wait/nofity需要配合synchronized使用
問題2:明明消費(fèi)者線程獲得了鎖,并沒走完synchronized方法秒啦,生產(chǎn)者是如何進(jìn)入到synchronized的熬粗?
問題3:生產(chǎn)者是如何通知消費(fèi)者的?
問題4:生產(chǎn)者在調(diào)用notify的時候余境,消費(fèi)者為何并沒有被喚醒驻呐?

synchronized:代碼塊通過javap生成的字節(jié)碼中包含 monitorenter 和 monitorexit 指令,執(zhí)行monitorenter指令可以獲取對象的monitor
查看Object.wait源碼芳来,上面有句注釋

This method should only be called by a thread that is the owner of this object's monitor

@問題1
Object.wait實(shí)際調(diào)用的是wait(0)含末;wait(0)上面的注釋寫到

This method causes the current thread (call it <var>T</var>) to  place itself in the wait set for this object and then to relinquish any and all synchronization claims on this object

意思就是wait方法會導(dǎo)致把當(dāng)前線程放到wait set隊列,并釋放所有monitor對象即舌,等待被喚醒
@問題2
ObjectMonitor:
每個線程都有ObjectMonitor對象佣盒,ObjectMonitor對象維護(hù)了free和used的objectMonitor對象列表,如果當(dāng)前free列表為空顽聂,將向全局global list請求分配ObjectMonitor
WaitSet :處于wait狀態(tài)的線程肥惭,會被加入到wait set;
EntryList:處于等待鎖block狀態(tài)的線程紊搪,會被加入到entry set务豺;

ObjectWaiter:
ObjectWaiter對象是雙向鏈表結(jié)構(gòu),保存了_thread(當(dāng)前線程)以及當(dāng)前的狀態(tài)TState等數(shù)據(jù)嗦明, 每個等待鎖的線程都會被封裝成ObjectWaiter對象笼沥。

wait方法實(shí)現(xiàn)

lock.wait()方法最終通過ObjectMonitor的void wait(jlong millis, bool interruptable, TRAPS);實(shí)現(xiàn):
1、將當(dāng)前線程封裝成ObjectWaiter對象;
2奔浅、通過ObjectMonitor::AddWaiter方法將ObjectWaiter添加到_WaitSet列表中馆纳;
3、通過ObjectMonitor::exit方法釋放當(dāng)前的ObjectMonitor對象汹桦,這樣其它競爭線程就可以獲取該ObjectMonitor對象鲁驶。
4、最終底層的park方法會掛起線程舞骆;
@問題3

notify:
lock.notify()方法最終通過ObjectMonitor的void notify(TRAPS)實(shí)現(xiàn):
1钥弯、如果當(dāng)前_WaitSet為空,即沒有正在等待的線程督禽,則直接返回脆霎;
2、通過ObjectMonitor::DequeueWaiter方法狈惫,獲取_WaitSet列表中的第一個ObjectWaiter節(jié)點(diǎn)睛蛛,實(shí)現(xiàn)也很簡單。
這里需要注意的是胧谈,在jdk的notify方法注釋是隨機(jī)喚醒一個線程忆肾,其實(shí)是第一個ObjectWaiter節(jié)點(diǎn)
3、根據(jù)不同的策略菱肖,將取出來的ObjectWaiter節(jié)點(diǎn)客冈,加入到_EntryList或則通過Atomic::cmpxchg_ptr指令進(jìn)行自旋操作cxq,具體代碼實(shí)現(xiàn)有點(diǎn)長稳强,這里就不貼了郊酒,有興趣的同學(xué)可以看objectMonitor::notify方法;
@問題4
notifyAll:
lock.notifyAll()方法最終通過ObjectMonitor的void notifyAll(TRAPS)實(shí)現(xiàn):
通過for循環(huán)取出_WaitSet的ObjectWaiter節(jié)點(diǎn)键袱,并根據(jù)不同策略,加入到_EntryList或則進(jìn)行自旋操作摹闽。

從JVM的方法實(shí)現(xiàn)中蹄咖,可以發(fā)現(xiàn):notify和notifyAll并不會釋放所占有的ObjectMonitor對象,其實(shí)真正釋放ObjectMonitor對象的時間點(diǎn)是在執(zhí)行monitorexit指令付鹿,一旦釋放ObjectMonitor對象了澜汤,entry set中ObjectWaiter節(jié)點(diǎn)所保存的線程就可以開始競爭ObjectMonitor對象進(jìn)行加鎖操作了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末舵匾,一起剝皮案震驚了整個濱河市俊抵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌坐梯,老刑警劉巖徽诲,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡谎替,警方通過查閱死者的電腦和手機(jī)偷溺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钱贯,“玉大人挫掏,你說我怎么就攤上這事≈让” “怎么了尉共?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長弃锐。 經(jīng)常有香客問我袄友,道長,這世上最難降的妖魔是什么拿愧? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任杠河,我火速辦了婚禮,結(jié)果婚禮上浇辜,老公的妹妹穿的比我還像新娘券敌。我一直安慰自己,他們只是感情好柳洋,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布待诅。 她就那樣靜靜地躺著,像睡著了一般熊镣。 火紅的嫁衣襯著肌膚如雪卑雁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天绪囱,我揣著相機(jī)與錄音测蹲,去河邊找鬼。 笑死鬼吵,一個胖子當(dāng)著我的面吹牛扣甲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播齿椅,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼琉挖,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涣脚?” 一聲冷哼從身側(cè)響起示辈,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎遣蚀,沒想到半個月后矾麻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體纱耻,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年射富,在試婚紗的時候發(fā)現(xiàn)自己被綠了膝迎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡胰耗,死狀恐怖限次,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情柴灯,我是刑警寧澤卖漫,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站赠群,受9級特大地震影響羊始,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜查描,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一突委、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧冬三,春花似錦匀油、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至窝爪,卻和暖如春弛车,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蒲每。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工纷跛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人邀杏。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓贫奠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親淮阐。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

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

  • Java的wait()刁品、notify()學(xué)習(xí)三部曲由三篇文章組成泣特,內(nèi)容分別是: 一、通過閱讀openjdk8的源碼...
    程序yuan閱讀 345評論 0 0
  • 面向?qū)ο蟮娜齻€特征 封裝,繼承,多態(tài).這個應(yīng)該是人人皆知.有時候也會加上抽象. 多態(tài)的好處 允許不同類對象對同一消...
    Blizzard_liu閱讀 1,310評論 0 6
  • 本文出自 Eddy Wiki 挑随,轉(zhuǎn)載請注明出處:http://eddy.wiki/interview-java.h...
    eddy_wiki閱讀 2,075評論 0 14
  • 女子本弱状您,為母則剛,說明柔弱的本性會改變,但無知的善良和被道德感綁架的本性是否可以改變膏孟? 公公有點(diǎn)不舒服去醫(yī)院檢查...
    愛兒一米距閱讀 507評論 0 0
  • C++中的引用與C語言的指針的指針運(yùn)用 引言 其實(shí)早就想寫這個筆記了眯分,尤其是看到越來越多同學(xué)上課不怎么聽講課后多次...
    crabor閱讀 1,480評論 3 1