Java基礎day12筆記:線程間通信|線程間通信安全問題|等待喚醒機制|生產(chǎn)者消費者|停止線程

????01-多線程(線程間通信-示例代碼)

? ? ? ? 上一篇講的賣票的例子劫瞳,幾個線程同時在賣票刑棵,它們執(zhí)行的代碼是相同的。

? ? ? ? 現(xiàn)在假如有一個資源狠鸳,一個線程往里面存數(shù)據(jù)揣苏,一個線程往出取數(shù)據(jù),一進一出件舵,兩個線程同時進行卸察,這時它們運行的代碼是不一致的。

? ? ? ? 一個簡單的例子:

? ? ? ? 我們需要描述三部分內(nèi)容:

? ? ? ? 第一部分铅祸,資源的內(nèi)容坑质,則里有name和sex。

? ? ? ? 第二部分临梗,Input的方法涡扼。

? ? ? ? 第三部分,Output的方法盟庞。

? ? ? ? 線程間通訊:

? ? ? ? 其實就是多個線程在操作同一個資源吃沪,但是操作的動作不同。

? ? ? ? 將上面的例子用代碼表示出來~

? ? ? ? 進一步細化:

? ? ? ? 輸入:????????

? ? ? ? 輸出:

? ? ? ? 在主函數(shù)中創(chuàng)建對象并調(diào)用:

? ? ? ? 運行結(jié)果:

? ? ? ? 本來什猖,mike是man票彪,麗麗是女女女女女,但是我們發(fā)現(xiàn)卸伞,運行出來抹镊,mike有時候也是女女女女女,麗麗有時候也是man荤傲。

? ? ? ? 為什么會這樣呢垮耳?

? ? ? ? 分析:

????02-多線程(線程間通信-解決安全問題)

? ? ? ? 現(xiàn)在我們來解決剛剛發(fā)生的安全問題。

? ? ? ? 我們用同步代碼塊synchronized將被操作的代碼封裝起來:

? ? ? ? ?運行:

? ? ? ? 發(fā)現(xiàn)問題還在遂黍。

? ? ? ? 我們看看它是否滿足同步的兩個前提:

? ? ? ? 1终佛,兩個及兩個以上線程?不滿足雾家,這里只是一個線程铃彰,是輸入的那個線程,輸出的在另一個類中芯咧。所以只是同步了一個線程牙捉。

? ? ? ? 怎么辦~改呀~將另一個也同步起來:

? ? ? ? 運行,問題還是存在:

? ? ? ? 我們再看看同步的第二個前提它們是否滿足:

? ? ? ? 2敬飒,用的是同一個鎖邪铲? 不是的。

? ? ? ? 改起來无拗,我們隨便找一個對象带到,就都用Input吧:

? ? ? ? 編譯運行:

? ? ? ? 問題解決啦。

? ? ? ? 那么我們這個程序中共有四個類英染,寫Res.class揽惹、Output.class棘幸、InputOutputDemo當鎖都OK谊却。

? ? ? ? 其實還有一個對象是唯一的枫匾,就是主函數(shù)中創(chuàng)建的Res的對象r递览。

? ? ? ? 我們也可以這樣寫:

? ? ? ? Output類也是這樣寫哦。代碼略慕嚷。這樣也是完全OK的哦哥牍。

????03-多線程(線程間通信-等待喚醒機制)

? ? ? ? 為什么會出現(xiàn)大片的mike man和麗麗 女女女女女呢?

? ? ? ? 分析:Input搶到執(zhí)行權后喝检,輸入了mike man嗅辣,輸入完成后,接下來Input和Output都存在搶到執(zhí)行權的可能性挠说。并不是說這次是Input搶到了澡谭,下次就一定是Output搶到。

? ? ? ? 所以损俭,如果一直是Input搶到執(zhí)行權蛙奖,每次輸入的name和sex,都會將前一次的輸入內(nèi)容覆蓋掉杆兵。忽然雁仲,某一個時刻,Output搶到了執(zhí)行權琐脏。它會只輸出一次嗎攒砖?不是。CPU執(zhí)行它的時候日裙,它有可能會輸出多次吹艇,所以會出現(xiàn)一打印一大片的情況。

? ? ? ? 而我們的需求是昂拂,我輸入一個受神,你輸出一個。

? ? ? ? 為了滿足這個需求格侯,我們加一個flag標記鼻听,表示里面有沒有值。輸入線程在輸入數(shù)據(jù)的時候联四,先判斷flag是否為false撑碴,若是false,則代表里面沒有值碎连,輸入線程就在這里比如存一個mike nan灰羽。存完了以后驮履,輸入線程是不是還會持有執(zhí)行權鱼辙?它在存完數(shù)據(jù)之后廉嚼,就應該做一件事情,那就是將flag標記改為true倒戏,代表里面有數(shù)據(jù)怠噪。這時如果輸入線程再次拿到執(zhí)行權,而flag為true杜跷,它就不能往里存了傍念。

????????這個時候該怎么辦呢?我們是不是應該讓輸入線程在這里等著不要動呀葛闷?那就sleep一下吧憋槐。sleep多長時間合適呢?不確定呀淑趾。什么時候應該醒呢阳仔?

????????最靠譜的應該是輸出線程將數(shù)據(jù)取出后醒來。所以這個時候wait最合適啦扣泊!“你先等著別動喔近范,我叫你你再動喔⊙有罚”所以輸入線程就wait了评矩,一wait就凍結(jié)辣。

? ? ? ? 凍結(jié)的特點是什么呢阱飘?放棄執(zhí)行資格斥杜。

? ? ? ? 這時就只剩輸出線程可以爭奪執(zhí)行權啦,所以它拿到了執(zhí)行權俯萌,判斷flag為true果录,代表里面有東西,于是拿出了數(shù)據(jù)咐熙,然后將flag改為false弱恒。這個時候就應該叫一下輸入線程啦,“寶寶你該往里面存東西啦棋恼》档”

? ? ? ? 于是輸入線程和輸出線程就這樣交換著輸入--輸出,輸入線程和輸出線程也適時的等待對方存數(shù)據(jù)/取數(shù)據(jù)爪飘。

? ? ? ? 現(xiàn)在我們在講的义起,就是今天的重點:等待喚醒機制

? ? ? ? 這種情況非常常見~

? ? ? ? 程序怎么寫呢师崎?

? ? ? ? 如下:

? ? ? ? 給資源類中加一個flag標志:

? ? ? ? Input類中默终,添加判斷flag值的代碼,若為true則wait,若為false則存值齐蔽,存完值后將flag賦值為true两疚,并喚醒另一個線程:

? ? ? ? Output類中,添加判斷flag值的代碼含滴,若為false诱渤,則wait,若為true谈况,則輸出數(shù)據(jù)勺美,輸出數(shù)據(jù)后將flag賦值為false,并喚醒另一個線程:

? ? ? ? 對啦碑韵,notify只能喚醒一個赡茸,如果想喚醒好多個,還有一個方法祝闻,叫notifyAll坛掠。

? ? ? ? 不說其它的啦,我們?nèi)hread類中找一下我們需要用到的wait和notify方法治筒,發(fā)現(xiàn)它木有這些方法呀屉栓。后來又看到介個:

? ? ? ? 原來它們竟然都是從object繼承過來噠。

? ? ? ? 看一下wait:

? ? ? ? 我們發(fā)現(xiàn)它拋出異常啦耸袜。我們想使用wait的話友多,就只能try。

? ? ? ? 再看一下wait方法的描述:

? ? ? ? 因為只有同步的時候才需要鎖堤框,所以域滥,wait、notify蜈抓、notifyAll這幾個方法启绰,都是用在同步中噠~

? ? ? ? 而用在同步中,容易產(chǎn)生問題沟使,什么問題呢委可?

? ? ? ? 你必須要標識出這個wait,它所操作的線程所屬的鎖腊嗡。

? ? ? ? 這里的wait是指着倾,持有r這個鎖的線程。

? ? ? ? 為什么燕少?

? ? ? ? 因為同步會出現(xiàn)嵌套卡者。

? ? ? ? 是不是有兩個鎖呀?

? ? ? ? 而notify所notify的是r這個鎖所在的線程客们。

? ? ? ? 所以這里也要標識r哦:

? ? ? ? 同理崇决,Output方法也是:

? ? ? ? 可是材诽,為什么wait、notify這種用于操作線程的方法卻定義在了object當中呢恒傻?

? ? ? ? 我們回想一下岳守,鎖是不是可以是任意對象呀?

? ? ? ? 而任意對象可以調(diào)用的方法碌冶,是不是應該定義在我們的上帝類Object當中呢?

? ? ? ? 這下就全明白辣涝缝!

? ? ? ? 總結(jié)一下:

? ? ? ? wait扑庞、notify、notifyAll都使用在同步中拒逮,因為要對持有監(jiān)視器(鎖)的線程操作罐氨。

? ? ? ? 所以要使用在同步中,因為只有同步才具有鎖滩援。

? ? ? ? 為什么這些操作線程的方法要定義在Object類中呢栅隐?

? ? ? ? 因為這些方法在操作同步線程時,都必須要標識它們所操作線程持有的鎖玩徊。

? ? ? ? 只有同一個鎖上的被等待線程租悄,可以被同一個鎖上的notify喚醒。

? ? ? ? 不可以對不同鎖中的線程進行喚醒恩袱。

? ? ? ? 也就是說泣棋,等待和喚醒必須是同一個鎖。

? ? ? ? 而鎖可以是任意對象畔塔,所以可以被任意對象調(diào)用的方法定義在Object類中潭辈。

? ? ? ? ?好啦,說了這么多澈吨,我們編譯運行一下:

? ? ? ? OK~需求成功解決把敢,耶?(?òωó?)?

????04-多線程(線程間通信-代碼優(yōu)化)

? ? ? ? 剛剛程序?qū)懲炅耍覀儼l(fā)現(xiàn)一個問題谅辣,就是它的代碼沒有進行優(yōu)化修赞。

? ? ? ? 哪里沒有優(yōu)化捏?

? ? ? ? 跟我來~

? ? ? ? 1桑阶,進行數(shù)據(jù)的私有化榔组。

? ? ? ? 2,對數(shù)據(jù)私有化后联逻,要對外提供公共的訪問方法搓扯。

? ? ? ? 我們又發(fā)現(xiàn),在set中包归,對name和sex進行賦值的時候锨推,有可能出現(xiàn)安全問題,比如輸完name在這里停住了,還沒來得及輸入sex就被輸出線程搶走了cpu執(zhí)行權换可,這樣就會出問題哦椎椰。

? ? ? ? 所以需要將這兩個語句同步,而這個方法中只有這兩句話沾鳄,所以我們將函數(shù)同步就OK啦:

? ? ? ? 而set方法同步了慨飘,也得把out方法也同步了,因為同步的前提是兩個及以上的線程~

? ? ? ? 現(xiàn)在加入wait和notify:

? ? ? ? OK~

? ? ? ? 下面我們將舊的代碼中這一部分去掉译荞,沒用啦瓤的,Input類的run方法中,直接調(diào)用set就好:

? ? ? ? Output類的run方法中吞歼,直接調(diào)用out就好:

圈膏、

? ? ? ? 主函數(shù)中這樣寫,也簡化啦:

? ? ? ? 編譯運行篙骡,OK噠:

????05-多線程(線程間通信-生產(chǎn)者消費者)

? ? ? ? 這節(jié)課我們繼續(xù)用上次的例子稽坤,做一點小小的修改即可。

? ? ? ? 在上次的例子中糯俗,我們的名字和性別都是固定的尿褪,而且沒有編號。

? ? ? ? 這節(jié)課得湘,我們打算給每個輸入的數(shù)據(jù)都帶上編號茫多,每個輸出的也顯示輸出數(shù)據(jù)的編號,生產(chǎn)一個忽刽,消費一個天揖,生產(chǎn)一個,消費一個跪帝。就像生產(chǎn)者和消費者一樣今膊,所以這節(jié)課我們就來生產(chǎn)產(chǎn)品、消費產(chǎn)品啦伞剑。

? ? ? ? 資源Resource類:

? ? ? ? 生產(chǎn)者Producer類:

? ? ? ? 消費者Consumer類:

? ? ? ? 主函數(shù):

? ? ? ? 編譯運行:

? ? ? ? OK的哦斑唬。每生產(chǎn)一個,就會消費一個黎泣。????

? ? ? ? 我們的生產(chǎn)者和消費者可不止一個呢恕刘,再加一個生產(chǎn)者和一個消費者:

? ? ? ? 編譯運行:

? ? ? ? 我們發(fā)現(xiàn),有的時候生產(chǎn)了一個商品抒倚,卻被消費了兩次褐着。

????????還有時候會生產(chǎn)兩次卻消費一次:

? ? ? ? 為什么會出現(xiàn)這種現(xiàn)象呢?

? ? ? ? 我們先來分析托呕,生產(chǎn)兩次而消費一次是為什么含蓉。

? ? ? ? 假設生產(chǎn)者先獲取到了cpu的執(zhí)行權频敛,而生產(chǎn)者有兩個,t1馅扣、t2斟赚。假設t1獲取到了cpu的執(zhí)行權,這個過程有點小復雜喔:

? ? ? ? 因為篇幅原因差油,第(5)步之后我就不再畫啦拗军,改為文字陳述,不過理解起來問題應該也不大~

? ? ? ? 應該能夠注意到蓄喇,因為t1上次是因為判斷flag不合格而在原地wait发侵,所以(5)中t1被t3喚醒后,就跳過了判斷flag的流程公罕,直接生產(chǎn)(當然此時t3剛剛消費過,flag為false耀销,也是合格的)楼眷。到這里還沒出現(xiàn)問題。

? ? ? ? 出現(xiàn)問題的是下一步熊尉,t1生產(chǎn)完后罐柳,該喚醒下一個線程了,而此時等待喚醒的線程是t2狰住,同樣是生產(chǎn)者张吉。和t1一樣,它上次也是因為判斷flag不合格而在原地wait了催植,此時它被喚醒后肮蛹,也跳過了判斷flag的流程,一路直下開始生產(chǎn)创南,但這時就出現(xiàn)問題了喔伦忠。t1剛剛生產(chǎn)過,這個產(chǎn)品還沒有消費稿辙,flag的值也為true昆码,但是因為它跳過了判斷flag的步驟,所以造成了消費前的第二次生產(chǎn)邻储。

? ? ? ? t2生產(chǎn)完后赋咽,t1、t2吨娜、t3脓匿、t4都有可能獲得執(zhí)行權。假設t1宦赠、t2先獲得執(zhí)行權亦镶,但因為flag為true日月,它們終將wait。所以最后獲得執(zhí)行權的會是t3或者t4缤骨。此時會消費一次爱咬。

? ? ? ? 這時就發(fā)生了生產(chǎn)兩次,消費一次的情況绊起。

? ? ? ? 生產(chǎn)一次精拟,消費兩次的情況同理,不再贅述~

? ? ? ? 我們反思一下生產(chǎn)兩次消費一次的錯誤所在虱歪,如果t2被喚醒后蜂绎,能夠再次判斷一下flag的值,這個錯誤就不會發(fā)生了笋鄙。對于t1师枣、t3、t4也是同理萧落,它們都會遇到相同的處境践美。

? ? ? ? 那么,怎么才能夠讓它們每次醒過來都能再判斷一次呢找岖?

? ? ? ? if是不是只判斷一次陨倡,而如果換成while,就會判斷多次许布。所以我們將if換成while兴革。

? ? ? ? 但是編譯運行之后,我們會發(fā)現(xiàn)蜜唾,鎖死了杂曲,程序卡住了:

? ? ? ? 為什么呢?

? ? ? ? 這時全都等待了袁余,全凍結(jié)了解阅。(不太明白為什么全等待了?當flag為false時不就可以生產(chǎn)嗎泌霍?)

? ? ? ? t1 notify的時候货抄,有t2、t3朱转、t4在等待蟹地,t1將t2喚醒了,它將自己方(生產(chǎn)者)的喚醒了藤为,而沒有將對方(消費者)喚醒怪与,但是,是不是應該把對方喚醒才靠譜呀缅疟。

? ? ? ? 而notify往往喚醒的是線程池中的第一個分别,會導致數(shù)據(jù)錯亂遍愿,而加上while以后,會導致全部等待耘斩。(似乎有點明白剛剛的問題了沼填,假設wait列表中為t1、t2括授,再假設t1被喚醒坞笙,則t1生產(chǎn)完后,會喚醒t2荚虚,而此時t2判斷flag為true薛夜,會再次wait,假設t1再次搶到執(zhí)行權版述,依然會判斷flag為true梯澜,t1也被wait了。想到這里又不明貶了渴析,為什么會被鎖死呢晚伙?如果t1、t2此時又被wait了檬某,那么t3撬腾、t4消費后宏多,按理說它們還是會得到生產(chǎn)的機會呀顺囊?難道如果線程的等待列表中存在本方線程舱污,會默認主動喚醒本方線程?這時就會陷入死循環(huán)场斑,只有這個解釋可以讓我理解,不知道想的對不對牵署。

? ? ? ? 但是有一個notifyAll漏隐,不分本方它方,所有的線程都有機會被喚醒奴迅。

? ? ? ? 編譯運行:

? ? ? ? 檢查了一下青责,發(fā)現(xiàn)沒有再出現(xiàn)問題喔。

? ? ? ? 對于多個生產(chǎn)者和消費者取具,為什么要定義while判斷標記脖隶?

? ? ? ? 為了讓被喚醒的線程再一次判斷標記flag。

? ? ? ? 為什么定義notifyAll暇检?

? ? ? ? 因為需要喚醒對方線程产阱。只用notify,容易出現(xiàn)只喚醒本方線程的情況块仆,導致程序中的所有線程都等待构蹬。

? ? ? ? 總結(jié)一下:當生產(chǎn)者和消費者出現(xiàn)多個時王暗,判斷flag必須用while而不是之前的if,喚醒的時候必須用notifyAll(既喚醒本方庄敛,又喚醒對方)而不是notify俗壹。

? ??06-多線程(線程間通信-生產(chǎn)者消費者JDK5.0升級版)

? ? ? ? 上節(jié)課我們說用notifyAll,原因是想喚醒對方線程铐姚,但是伴隨著對方線程被喚醒策肝,本方線程也會同時被喚醒,也跟對方線程搶cpu隐绵。

? ? ? ? 而我們希望的是之众,只喚醒對方線程,不喚醒本方線程依许。該怎么去做呢棺禾?

? ? ? ? 后來Java工程師說,我們升級了一下JDK峭跳,提供了專有的解決方法膘婶。

? ? ? ? 下面就講一講升級后的新特性~?

? ? ? ? 升級后,在工具類中有一個java.util.concurrent.locks包:

? ? ? ? 這個包中給我們提供了一些常用的接口和類:

? ? ? ? 注意這里有一個Lock接口蛀醉,就是鎖的意思悬襟,它提供了什么東東呢?

? ? ? ? 看一下:

? ? ? ? 聽它這么描述拯刁,意思應該是lock可以替代synchronized耶脊岳。

? ? ? ? 那替代之后,怎么使用lock呢垛玻?

? ? ? ? 我們來看一下它的方法:?

? ? ? ? 之前synchronized加鎖和解鎖的過程我們都看不到割捅,而用lock之后,這個過程就變顯式啦帚桩,我們調(diào)用lock()來加鎖亿驾,調(diào)用unlock()來解鎖。

? ? ? ? JDK升級的過程中账嚎,JDK1.5的升級絕對是里程碑的升級莫瞬。早期n多年一直都在用JDK1.4,JDK1.5升級后郭蕉,把標識號疼邀、版本號都給改啦,改成了JDK5.0恳不,再往后就是JDK6.0檩小、JDK7.0。

? ? ? ? 而這個工具是1.5才有的烟勋,1.4的時候都沒見著這個呢规求,所以1.4的程序員多痛苦呀筐付,都在while、notifyAll阻肿。

? ? ? ? 而現(xiàn)在搞成了lock瓦戚,這就很爽~

? ? ? ? 那這里所說的,支持多個相關的Condition對象指的是什么呀丛塌?

? ? ? ? 那我們必須要用一下~

? ? ? ? 我們點擊進去Condition接口中看一下:

? ? ? ? 看完之后较解,我們知道了,1.5之后赴邻,synchronized掛了印衔,被lock替代了,Object姥敛、wait奸焙、notify、notifyAll方法也掛了彤敛,被Condition替代了与帆。

? ? ? ? Java可好啦,還給我們提供了示例:

? ? ? ? 主要關注一下紅色框住的地方哦墨榄,我們也來這樣寫一下~

? ? ? ? 那我們首先是不是應該先搞一個鎖呀玄糟?

? ? ? ? 而Lock是一個接口,它下面有很多實現(xiàn)類:

? ? ? ? 我們用ReentrantLock就好啦袄秩。后面什么讀鎖寫鎖的我們先不需要用~

? ? ? ? 那個示例中也有寫到喔:

? ? ? ? 我們也建立一個鎖:

? ? ? ? 對象里new有什么用呢阵翎?不用管~我們用的是外面的規(guī)則:Lock lock。

? ? ? ? wait播揪、notify方法都應該定義在同步語句塊當中贮喧,同步語句塊有鎖筒狠,而每一個wait猪狈、notify都要標識自己所屬的鎖。而現(xiàn)在同步變成了lock辩恼,wait雇庙、notify變成了condition,而condition 怎么獲取呢灶伊?是不是通過鎖獲冉啊?

? ? ? ? 而Lock這個接口當中聘萨,就定義了一些方法竹椒,是不是它可以幫我們建立一個newCondition:

? ? ? ? 根據(jù)鎖,建立一個具有wait米辐、notify功能的對象胸完,這個對象叫Condition书释。

? ? ? ? Condition也來啦:

? ? ? ? 寫上拿到鎖和釋放鎖的方法:

? ? ? ? 我們把之前的同步語句就變成了這兩個方法,把拿到鎖赊窥、釋放鎖分成兩個功能爆惧,這樣寫就更明顯啦。

? ? ? ? 我們繼續(xù)寫~下一步是判斷標記flag锨能,如果為true的話扯再,就要等待:

? ? ? ? 再繼續(xù)~生產(chǎn)完商品后,將標記flag改為true址遇,此時是不是應該喚醒啦:

? ? ? ? 注意看一下熄阻,我們的程序有個小問題喔。

? ? ? ? 拿到鎖之后倔约,如果在執(zhí)行被鎖的代碼時拋出了異常饺律,這個功能是不是就結(jié)束了呢?而此時還沒有執(zhí)行到unlock跺株,所以這個鎖還被拿著复濒,還沒有被釋放,這就壞事啦乒省。

? ? ? ? 所以巧颈,unlock這句話一定要執(zhí)行,我們就要把它寫進finally中袖扛。

? ? ? ? 示例中也已經(jīng)寫好啦:

? ? ? ? 我們也按示例中這樣寫:

? ? ? ? 生產(chǎn)者方法set寫好啦砸泛,消費者方法out也是同理:

? ? ? ? 生產(chǎn)者類中調(diào)用生產(chǎn)者方法:

? ? ? ? 消費者類中調(diào)用消費者方法:

? ? ? ? 別忘了導入包包哦:

? ? ? ? 編譯運行:

? ? ? ? 程序掛這兒了。

? ? ? ? 注意蛆封,這個時候又回到了上節(jié)課那個問題解決之前唇礁,就是線程都等著啦,程序卡死啦惨篱。

? ? ? ? 我們將signal都改成signalAll:

? ? ? ? 再編譯運行盏筐,發(fā)現(xiàn)一切都OK啦。

? ? ? ? 但是現(xiàn)在還是會喚醒本方砸讳,我們希望它只喚醒對方琢融,不喚醒本方。接下來就顯示出新特性出現(xiàn)的好處啦:一個鎖上可以有多個相關的Condition對象簿寂。

? ? ? ? 然后在生產(chǎn)者方法set中漾抬,就可以讓生產(chǎn)者睡眠,后面喚醒消費者常遂,都可以直接指定的喲:

? ? ? ? 消費者方法out中也是一個道理纳令,讓消費者睡眠,喚醒生產(chǎn)者:

? ? ? ? condition_pro.await()只能被condition_pro.signal()喚醒,condition_con.await()只能被condition_con.signal()喚醒平绩。

? ? ? ? 這就是JDK1.5中提供的多線程升級解決方案坤按。

? ? ? ? 將同步Synchronized替換成現(xiàn)實Lock操作。

? ? ? ? 將Object中的wait馒过,notify臭脓,notifyAll,替換成了Condition對象腹忽。

? ? ? ? 該對象可以通過Lock鎖進行獲取来累。

? ? ? ? 在該實例中,實現(xiàn)了本方只喚醒對方的操作窘奏。

? ? ? ? 以后問生產(chǎn)者消費者有什么替代方案嘹锁,就說1.5版本以后,它提供了顯式的鎖機制以及顯式的鎖對象等待喚醒操作機制着裹。同時领猾,它把等待喚醒機制封裝了,封裝完骇扇,一個鎖對應多個condition摔竿。(之前一個鎖只能對應一個wait/notify)

????07-多線程(停止線程)

? ? ? ? 接下來說一下停止線程。

? ? ? ? 線程中一般都會寫循環(huán)少孝,如果不寫循環(huán)就執(zhí)行一句話也沒有必要開多線程啦继低,單線程一樣能搞定。所以呢稍走,我們玩的就是線程的運行袁翁。

? ? ? ? 但是運行了半天,我們該怎么讓線程停下來呢婿脸?

? ? ? ? 之前我們記得有stop方法粱胜。

? ? ? ? 我們?nèi)hread類中找一下:

? ? ? ? 但是很遺憾,它已經(jīng)過時了狐树。

? ? ? ? 既然已經(jīng)過時了焙压,為什么不清楚掉它呢?因為老的程序中可能還會有褪迟。

? ? ? ? 現(xiàn)在不用它的原因是冗恨,這個方法它有一些bug答憔,這個bug是味赃,它是強制性的停止,不管什么時候都會強制停掉線程虐拓,這是不OK的心俗。

? ? ? ? 同樣過時的還有suspend方法:

? ? ? ? 它一掛起會發(fā)生死鎖。

? ? ? ? 所以說它們都過時了。

? ? ? ? 那我們現(xiàn)在該怎樣讓線程停下來呢城榛?

? ? ? ? 只有一種揪利,run方法結(jié)束。(線程要運行的代碼沒有了狠持,線程也就結(jié)束了疟位。)

? ? ? ? 該怎么結(jié)束run方法呢?

? ? ? ? 開啟多線程運行喘垂,運行代碼通常是循環(huán)結(jié)構(gòu)甜刻。

? ? ? ? 只要控制住循環(huán),就可以讓run方法結(jié)束正勒,也就是線程結(jié)束得院。

? ? ? ? 試一下~

? ? ? ? 主函數(shù)中:

? ? ? ? 編譯運行:

? ? ? ? (不太懂這個結(jié)果

? ? ? ? 只要能讓循環(huán)結(jié)束,這個線程就能結(jié)束章贞。

? ? ? ? 但是有一種特殊情況祥绞,這種特殊情況下,程序也停不下來:

? ? ? ? 編譯運行:

? ? ? ? 程序沒停下來鸭限。但它不是死循環(huán)蜕径,現(xiàn)在代碼并沒有消耗資源。

? ? ? ? 當主線程while(true)的時候败京,循環(huán)一直在轉(zhuǎn)丧荐。num++==60后,st.changeFlag();喧枷『缤常可是開啟兩個線程以后,這兩個線程無論什么時候搶到cpu執(zhí)行權隧甚,都會在這里面運行:

? ? ? ? 線程0一進來车荔,拿到鎖了,但是try之后就wait了戚扳,釋放了資格忧便。緊接著線程1進來也wait了,釋放了資格∶苯瑁現(xiàn)在它們倆就掛在這里不動了珠增。

? ? ? ? 主線程執(zhí)行完了嗎?執(zhí)行完了砍艾。

? ? ? ? 我們在主線程中再加一個over做標記:

? ? ? ? 主線程也執(zhí)行完了蒂教,現(xiàn)在還有兩個線程存活。

? ? ? ? 這個就是問題脆荷。改變了標記凝垛,但是沒有結(jié)束線程懊悯。

? ? ? ? 特殊情況:

? ? ? ? 當線程出獄了凍結(jié)狀態(tài),就不會讀取到標記梦皮,那么線程就不會結(jié)束炭分。

? ? ? ? 當沒有指定的方式讓凍結(jié)的線程恢復到運行狀態(tài)時,這時需要對凍結(jié)狀態(tài)進行清除剑肯。強制讓線程恢復到運行狀態(tài)中來捧毛,這樣就可以操作標記讓線程結(jié)束。

? ? ? ? Thread類提供該方法让网,叫interrupt()岖妄。

? ? ? ? 那該怎么解決問題呢?

? ? ? ? 像這種狀況發(fā)生之后寂祥,我們可以強制解決問題荐虐。

? ? ? ? 在Thread類中給我們提供了一個方法:interrupt()。

? ? ? ? 點進去看一下:

? ? ? ? 解釋一下哦丸凭,中斷狀態(tài)絕對不是停止線程福扬,stop方法才是停止線程。

? ? ? ? 而中斷線程的意思是惜犀,當進入到凍結(jié)狀態(tài)時铛碑,相當被掛起了,動不了了虽界,中斷線程強制清除這個凍結(jié)狀態(tài)汽烦,讓它恢復到運行狀態(tài)中來。

? ? ? ? #Java小劇場

? ? ? ? 小楠wait了莉御,有人用notify輕輕拍了一下小楠撇吞,小楠就醒了。

? ? ? ? 小楠sleep了礁叔,5分鐘后自己醒來了(假設sleep時間設置為5分鐘)牍颈。

? ? ? ? 小楠掛過去了,有人用板磚把小楠拍醒了琅关,可是小楠受傷了煮岁,發(fā)生了受傷異常。

? ? ? ? #

? ? ? ? 我們對t1下手了:

? ? ? ? Thread0異常了:

? ? ? ? Thread0拋出了中斷異常涣易,被catch捕獲了画机。

? ? ? ? 凍結(jié)狀態(tài)強制被清除,它就發(fā)生異常了新症。異常被catch住并解決了(打印...Exception)步氏,然后又回到while(flag),又被wait了账劲。t2還是沒有解決戳护。

? ? ? ? 我們對t2也解決一下:

? ? ? ? 編譯運行:

? ? ? ? 現(xiàn)在Thread0和Thread1都中磚頭了金抡,但是程序還是沒有停下來瀑焦,因為它們又回去等待去了腌且。

? ? ? ? 但是想一想,能夠讓那個它們回到運行狀態(tài)榛瓮,是不是離結(jié)束就不遠了铺董?

? ? ? ? 怎么結(jié)束呢?

? ? ? ? 只要能發(fā)生異常禀晓,是不是代表著有人在強制清除凍結(jié)狀態(tài)精续,目的就是想讓它結(jié)束,所以我們在這里將flag設為flase:

? ? ? ? 編譯運行:

? ? ? ? 程序結(jié)束啦粹懒。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末重付,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子凫乖,更是在濱河造成了極大的恐慌确垫,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帽芽,死亡現(xiàn)場離奇詭異删掀,居然都是意外死亡,警方通過查閱死者的電腦和手機导街,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門披泪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人搬瑰,你說我怎么就攤上這事款票。” “怎么了泽论?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵徽职,是天一觀的道長。 經(jīng)常有香客問我佩厚,道長姆钉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任抄瓦,我火速辦了婚禮潮瓶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钙姊。我一直安慰自己毯辅,他們只是感情好,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布煞额。 她就那樣靜靜地躺著思恐,像睡著了一般沾谜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胀莹,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天基跑,我揣著相機與錄音,去河邊找鬼描焰。 笑死媳否,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的荆秦。 我是一名探鬼主播篱竭,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼步绸!你這毒婦竟也來了掺逼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤瓤介,失蹤者是張志新(化名)和其女友劉穎吕喘,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惑朦,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡兽泄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了漾月。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片病梢。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖梁肿,靈堂內(nèi)的尸體忽然破棺而出蜓陌,到底是詐尸還是另有隱情,我是刑警寧澤吩蔑,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布钮热,位于F島的核電站,受9級特大地震影響烛芬,放射性物質(zhì)發(fā)生泄漏隧期。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一赘娄、第九天 我趴在偏房一處隱蔽的房頂上張望仆潮。 院中可真熱鬧,春花似錦遣臼、人聲如沸性置。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鹏浅。三九已至嗅义,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間隐砸,已是汗流浹背之碗。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留凰萨,地道東北人继控。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓械馆,卻偏偏與公主長得像胖眷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子霹崎,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

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