大話Java線程通信

“怎么還慢吞吞的!”但两,JVM翹著二郎腿在調度室里大聲喊道鬓梅。“Thread-2谨湘,你已經被我創(chuàng)建出來了绽快,趕緊干活!”紧阔。

啟動三個線程坊罢,一個生產者Thread-2,兩個消費者Thread-0和Thread-1

Thread-2這才反應過來擅耽,看看自己的身體活孩,是個結構分明、線條優(yōu)美的線程棧乖仇。先看看自己有啥東西:一個程序計數(shù)器憾儒、一連串的函數(shù)棧幀。程序計數(shù)器記錄程序執(zhí)行到哪里了乃沙,函數(shù)棧幀是一個方法的信息體起趾,里面包含局部變量表、操作數(shù)棧等警儒。


線程示意圖

“好的训裆,老板。我看到我有一個run方法,里面調用了produce()方法缭保,用人類的話說汛闸,我應該是個生產者啊”。JVM點了點頭艺骂,這小子挺機靈的诸老。“那你趕緊去生產產品吧钳恕,消費者線程打電話催了好幾次了”别伏。


我是個生產者線程

生產者線程的run方法

消費者線程,那是個啥忧额?

跟你一樣厘肮,也是由我創(chuàng)建出來的一個線程。只是職責與你不同睦番,你負責生產產品类茂,它負責消費產品。

消費者線程

它在哪里托嚣,我怎么看不見巩检。

你們彼此都是有獨立空間的,但是你們需要協(xié)作完成工作示启,你得注意兩點兢哭,①操作臨界區(qū)數(shù)據(jù)時要進行互斥(同一時刻只能有一個線程操作這個共享變量),②修改后你得通知其他線程可以操作這個共享變量了夫嗓;

你叫Producer類迟螺,你有一個放在方法區(qū)的實例變量taskQueue(List<Integer> taskQueue),你往這個容器中放東西舍咖,消費者線程的Consumer類也要持有同一個實例變量矩父,這樣它們就可以從這個容器里取產品了。JVM怕它不懂谎仲,多解釋了幾句浙垫。

看老大停頓了,天性活潑多問的Thread-2正準備開口接著問...

“先調用produce()函數(shù)棧幀郑诺,走一遍指令再說”。JVM沒搭理它杉武,發(fā)出了指令辙诞。

生產產品

過了1ms,活潑的Thread-2又打電話來了轻抱。我已經走了一遍了飞涂,剛開始讓我判斷是否生產到5個了,如果到了就調用wait()方法。因為數(shù)量為0所以就繼續(xù)走下面的邏輯较店,生產了一個產品放在taskQueue中士八,然后調用了notify()方法。調用這兩個方法的時候梁呈,到底發(fā)生了什么盎槎取?

你這大大咧咧的性格官卡,怎么忘了描述最重要的那點了呢蝗茁?

Thread-2摸摸自己紅撲撲的臉蛋,生怕老板罵它寻咒。

你剛開始運行的時候哮翘,系統(tǒng)是不是讓你去搶占taskQueue的monitor,你首先得搶到這個實例變量的monitor毛秘,你才能運行饭寺。才有后面調用wait()或notify()的事。
這些都是synchronized在起作用叫挟,它的作用是在代碼塊前后加上monitorenter和monitorexit兩個字節(jié)碼指令艰匙,這樣就不會有其他線程同時操作taskQueue這個變量。這個就是我們常說的互斥鎖霞揉。當然synchronized可以加在方法上或者包裹一個代碼塊旬薯,那說來就話長了。

我想起來了适秩,我在taskQueue的對象頭中確實看到了是我持有了鎖绊序。執(zhí)行taskQueue.wait()或taskQueue.notify()方法的時候,是需要持有這個對象的monitor的秽荞。


對象的內存布局示意圖

是的骤公,其中wait()、notify()這樣的方法是要在synchronized關鍵字包裹的中才能執(zhí)行的扬跋,否則會拋出IllegalMonitorStateException異常阶捆。

那這兩個方法的作用是啥咧?

你先生產到5個產品钦听。

JVM剛準備看會新聞洒试,Thread-2來電了。我生產5個了朴上,調用了wait()方法垒棋,釋放了monitor。我現(xiàn)在身子就好像僵硬了一樣痪宰,什么都干不了叼架。JVM哈哈哈一笑畔裕,你這是把自己放在了一個等待池中,等待taskQueue實例變量的鎖乖订。讓我跟你說說現(xiàn)在外面都發(fā)生了什么吧扮饶。

還記得你生產第一個產品的時候,就調用了notify()方法吧乍构。這個方法會通知其他的線程來消費產品甜无。但是他們沒有進來消費,因為你還沒有釋放taskQueue的monitor蜡吧,等到你生產滿5個后毫蚓,會調用wait()方法,這個方法會讓你釋放掉monitor昔善,并進入等待池元潘。
消費者線程那邊也是同樣的邏輯,他們會在收到通知后去競爭taskQueue的monitor君仆,競爭到的線程開始進入consume()方法翩概,它同樣需要加互斥鎖。當他們消費完了之后返咱,會調用wait()方法钥庇,你就有機會去爭奪taskQueue的monitor。搶到以后咖摹,你就可以繼續(xù)工作了评姨。

消費產品

最終的結果你可以看下:

執(zhí)行結果

Thread-0先運行,發(fā)現(xiàn)集合為空萤晴,則進入等待池吐句。生產者生產產品,達到5個后進入wait狀態(tài)店读,釋放taskQueue的monitor嗦枢,消費者線程Thread-0獲得taskQueue的monitor進入運行狀態(tài)。以此類推消費線程Thread-1的行為屯断。

JVM似乎很中意這個剛出生的娃娃文虏。“看你天賦異稟殖演,老哥賜你錦囊一幅氧秘,當你迷茫的時候,記得拿出來看看”趴久。

錦囊第一法:搬完磚了怎么休息敏储?

調用wait方法,它是Object類的方法朋鞍,final native修飾已添,即本地方法,不可被子類重寫滥酥!必須在sychronized塊中調用更舞;
作用:當前線程Thread T釋放對象鎖,并將自己放在等待池(wait set)中坎吻。直到其他線程獲取這個對象的monitor控制權缆蝉,以及發(fā)生以下四種情況之一時,線程Thread T將被喚醒:
①其他線程持有同一個對象的monitor并調用notify()方法
②其他線程持有同一個對象的monitor并調用notifyAll()方法
③超過等待時間
④被其他線程打斷

調用wait方法的要點:

  • 當前線程必須擁有這個對象的monitor瘦真,否則會拋出IllegalMonitorStateException異常刊头;
  • 當前線程將自己放在wait set中,并解除所有在這個對象上的同步聲明诸尽;
  • 當前線程調用wait方法后返回原杂,線程及對象的同步狀態(tài)與調用wait方法時是一致的;
  • 當被喚醒時您机,會與其他線程一起競爭獲取對象的同步權(獲取monitor)穿肄;

怎么用?

線程的喚醒緣故不一定是上面提到的四種情況际看,有時候可能會是假喚醒狀態(tài)咸产,所以需要在輪詢+條件判斷的代碼塊中使用,在不滿足條件時仲闽,讓線程一直等待:

    synchronized (obj) {
        while (condition does not hold)
        obj.wait(timeout);
         ... // Perform action appropriate to condition
     }

錦囊第二法:怎么通知其他線程小伙伴脑溢?

調用notify()或者notifyAll()方法。它們都是Object類的final native方法赖欣,必須在sychronized塊中調用屑彻;
作用:notify是喚醒等待相同對象monitor的其中一個線程Thread T(Thread T須調用了wait方法),至于是哪個線程被喚醒畏鼓,這要取決于具體實現(xiàn)酱酬,我們無法判斷。notifyAll是喚醒所有等待相同對象monitor的線程們云矫。其他跟notify一樣膳沽。

Thread T不會立馬進入運行狀態(tài):

①當前調用了notify()方法后,當前線程不一定會立馬釋放對象的monitor让禀,直到當前線程的同步塊執(zhí)行完成后才會釋放挑社;
②Thread T得到可以喚醒的通知,對象的monitor也被釋放了巡揍,此時會與其他等待當前對象monitor的對象一同競爭這個monitor痛阻,獲取到這個對象的monitor后方可進入運行狀態(tài)。

像這樣用就可以了:

   synchronized(lockObject){
      //establish_the_condition;
      lockObject.notify();
      //any additional code if needed
}

Thread-2就這樣慢慢熟悉了如何與其他線程進行協(xié)作腮敌,保證數(shù)據(jù)安全地運行阱当,后來它了解到人類為了寫出線程安全的代碼俏扩,需要考慮到原子性、可見性弊添、有序性等录淡。看來又有新東西可以學了油坝。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末嫉戚,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子澈圈,更是在濱河造成了極大的恐慌彬檀,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞬女,死亡現(xiàn)場離奇詭異窍帝,居然都是意外死亡,警方通過查閱死者的電腦和手機拆魏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門盯桦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人渤刃,你說我怎么就攤上這事拥峦。” “怎么了卖子?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵略号,是天一觀的道長。 經常有香客問我洋闽,道長玄柠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任诫舅,我火速辦了婚禮羽利,結果婚禮上,老公的妹妹穿的比我還像新娘刊懈。我一直安慰自己这弧,他們只是感情好,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布虚汛。 她就那樣靜靜地躺著匾浪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪卷哩。 梳的紋絲不亂的頭發(fā)上蛋辈,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機與錄音将谊,去河邊找鬼冷溶。 笑死渐白,一個胖子當著我的面吹牛,可吹牛的內容都是我干的挂洛。 我是一名探鬼主播礼预,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼虏劲!你這毒婦竟也來了?” 一聲冷哼從身側響起褒颈,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤柒巫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后谷丸,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體堡掏,經...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年刨疼,在試婚紗的時候發(fā)現(xiàn)自己被綠了泉唁。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡揩慕,死狀恐怖亭畜,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情迎卤,我是刑警寧澤拴鸵,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站蜗搔,受9級特大地震影響劲藐,放射性物質發(fā)生泄漏。R本人自食惡果不足惜樟凄,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一聘芜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧缝龄,春花似錦汰现、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至百新,卻和暖如春企软,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背饭望。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工仗哨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留形庭,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓厌漂,卻偏偏與公主長得像萨醒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子苇倡,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內容

  • 本文主要講了java中多線程的使用方法富纸、線程同步、線程數(shù)據(jù)傳遞旨椒、線程狀態(tài)及相應的一些線程函數(shù)用法晓褪、概述等。 首先講...
    李欣陽閱讀 2,456評論 1 15
  • Java多線程學習 [-] 一擴展javalangThread類 二實現(xiàn)javalangRunnable接口 三T...
    影馳閱讀 2,959評論 1 18
  • 本文出自 Eddy Wiki 综慎,轉載請注明出處:http://eddy.wiki/interview-java.h...
    eddy_wiki閱讀 2,128評論 0 14
  • 人生的大多數(shù)遇見 都會不可避免的分離 愿你在我看不見的地方 過得比從前更好
    冰玄海棠閱讀 93評論 0 1
  • Lamp環(huán)境下配置域名 本地安裝的虛擬機我們可以通過ifconfig命令來查看ip涣仿,通過ip在瀏覽器中訪問我們頁面...
    曹淵說創(chuàng)業(yè)閱讀 1,339評論 0 0