Java基礎(chǔ)day11筆記:多線程同步函數(shù)|synchronized的幾種鎖|死鎖

????11-多線程(多線程-同步函數(shù))

????????銀行類:

? ? ? ? 儲戶類:

? ? ? ? 主函數(shù)中開兩個線程:

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

? ? ? ? 但是多運(yùn)行幾次,發(fā)現(xiàn)順序不太對:

? ? ? ? 如何找問題坝疼?

? ? ? ? 1涌矢,明確哪些代碼是多線程運(yùn)行代碼按摘。

? ? ? ? 2,明確共享數(shù)據(jù)濒旦。

? ? ? ? 3株旷,明確多線程運(yùn)行代碼中哪些語句是操作共享數(shù)據(jù)的。

? ? ? ? 我們就來一條一條找:

? ? ? ? 1,哪些代碼是多線程運(yùn)行代碼:

? ? ? ? 2晾剖,共享數(shù)據(jù)锉矢。

? ? ? ? 3,多線程運(yùn)行代碼中哪些語句是操作共享數(shù)據(jù)的:

? ? ? ? run方法中只有一句應(yīng)該不是問題所在齿尽,add方法中有兩句沽损,我們來分析一下:

? ? ? ? 第一個線程進(jìn)入add,給sum加了n循头,還沒來的及打印绵估,下一個就占用了cpu,也進(jìn)入了add卡骂,做給sum加n的動作国裳,它加完打印完之后,剛剛那個沒來得及打印的線程才獲得了cpu的執(zhí)行權(quán)全跨,繼續(xù)打印缝左,這個時候,就會導(dǎo)致打印的順序不對浓若。

? ? ? ? 為了驗(yàn)證渺杉,我們sleep一下:

? ? ? ? 編譯運(yùn)行:

? ? ? ? 天啦嚕,問題好嚴(yán)重挪钓!

? ? ? ? 我們要解決問題少办!

? ? ? ? 編譯運(yùn)行:

? ? ? ? 問題解決啦。

? ? ? ? 那我們可以把這部分鎖著嗎诵原?

? ? ? ? 也不是不可以,但又一個問題挽放,就是如果李四進(jìn)來了绍赛,得等到他把300塊錢全存完,張三才能進(jìn)來辑畦。

? ? ? ? 所以吗蚌,哪些代碼該同步,哪些代碼不該同步纯出,一定要搞清楚蚯妇。

? ? ? ? 怎么搞清楚呢?就剛剛那三個步驟暂筝。

? ???????如何找問題箩言?

? ??????1,明確哪些代碼是多線程運(yùn)行代碼焕襟。

? ??????2陨收,明確共享數(shù)據(jù)。

? ? ? ? 3,明確多線程運(yùn)行代碼中哪些語句是操作共享數(shù)據(jù)的务漩。

? ? ? ? 接下來要講新知識點(diǎn)啦拄衰。

? ? ? ? 我們思考一下:

? ? ? ? 同步代碼塊是用來封裝代碼的,函數(shù)也是用來封裝代碼的饵骨,那它們有什么不同呢翘悉?

? ? ? ? 同步代碼塊相對比函數(shù),是擁有了同步性居触,那我們也讓函數(shù)具有同步性妖混,這個點(diǎn)子怎么樣?

? ? ? ? 其實(shí)超級簡單饼煞,把synchronized關(guān)鍵作為修飾符放在函數(shù)上就OK啦:

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

? ? ? ? 同步有兩種表現(xiàn)形式源葫,第一個是同步代碼塊,第二個是同步函數(shù)砖瞧。

? ??12-多線程(多線程-同步函數(shù)的鎖是this)

? ? ? ? 我們現(xiàn)在把之前賣票的程序也改成同步函數(shù)玩一下~

? ? ? ? 編譯運(yùn)行:

? ? ? ? 我們發(fā)現(xiàn)息堂,一直是0線程在做這件事情,1块促、2荣堰、3線程根本沒啟動。

? ? ? ? 我們分析一下竭翠,0進(jìn)來了振坚,1、2斋扰、3被鎖外面了渡八,而0一進(jìn)來就一直循環(huán)直到結(jié)束,1传货、2屎鳍、3根本沒有機(jī)會:

? ? ? ? 所以這么做不可以,因?yàn)闆]有搞清楚哪些需要同步问裕,哪些不需要逮壁。

? ? ? ? 那該怎么寫呢?單獨(dú)寫一個函數(shù):

? ? ? ? 這個問題就解決啦粮宛。

? ? ? ? 但是另一個問題來了窥淆,我們這里已經(jīng)沒有object對象了,那同步函數(shù)用的是哪個鎖巍杈?

? ? ? ? show方法是不是需要被對象調(diào)用的呀忧饭?那同步函數(shù)能夠用到的鎖,就是調(diào)用它的對象:this秉氧。

? ? ? ? 因?yàn)楹瘮?shù)需要被對象調(diào)用眷昆,那么函數(shù)都有一個所屬對象引用,就是this。

? ? ? ? 所以同步函數(shù)使用的鎖是this亚斋。

? ? ? ? 我們現(xiàn)在來驗(yàn)證一下:

? ? ? ? 使用兩個線程來賣票作媚。一個線程在同步代碼塊中,一個線程在同步函數(shù)中帅刊,都在執(zhí)行賣票動作纸泡。

? ? ? ? 如果要是同步的話,就不會出現(xiàn)錯誤赖瞒。

? ? ? ? 稍微修改一下代碼:

? ? ? ? 同步代碼塊:

? ? ? ? 同步函數(shù):

? ? ? ? 主函數(shù)總開啟兩個線程:

? ? ? ? 編譯運(yùn)行:

? ? ? ? 0線程也有女揭,1線程也有,可是為什么全都是show栏饮?code沒打印吧兔。

? ? ? ? 分析一下:現(xiàn)在有三個線程:主線程,0線程袍嬉,1線程境蔼。

? ? ? ? 主線程啟動后,創(chuàng)建了兩個線程對象伺通,然后開啟了0線程箍土,開啟完之后鸽捻,0線程會立刻執(zhí)行嗎褥傍?不一定,它先是處于臨時狀態(tài)懈凹,有了資格但是還沒有執(zhí)行權(quán)弓柱,因?yàn)檫@個時候是主線程持有執(zhí)行權(quán)沟堡。主線程有可能瞬間把這幾句話執(zhí)行完:

? ? ? ? 瞬間幾句話執(zhí)行完后,flag值為false矢空。

? ? ? ? 主線程執(zhí)行完t2.start()之后弦叶,就結(jié)束了。

? ? ? ? 現(xiàn)在就剩兩個線程:0線程和1線程妇多,它們開始運(yùn)行了。

? ? ? ? 因?yàn)閒lag值為false燕侠,所以0線程和1線程都去show中執(zhí)行了者祖。

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

? ? ? ? 想讓主線程執(zhí)行完t1.start()之后先停一下绢彤,讓0線程運(yùn)行一會兒:

? ? ? ? 注意七问,在主線程睡眠的這10ms內(nèi),能運(yùn)行的線程只有0線程茫舶,這個時候他就去執(zhí)行同步代碼塊了械巡。

? ? ? ? 過了10ms,主線程醒了。醒完以后讥耗,它繼續(xù)往下執(zhí)行有勾,將flag置為假,開啟了1線程古程,1線程就去執(zhí)行同步函數(shù)show了蔼卡。

? ? ? ? 此時0線程和1線程同時運(yùn)行。

? ? ? ? 編譯運(yùn)行:

? ? ? ? 搞定挣磨!

? ? ? ? 但是這里出現(xiàn)錯票了:

? ? ? ? 因?yàn)槠睌?shù)只能到1雇逞,出現(xiàn)賣0號票就錯了。

? ? ? ? 所以不安全茁裙。

? ? ? ? 可是明明加了同步塘砸,怎么就能不安全呢?

? ? ? ? 加了同步還沒解決晤锥,肯定是兩個前提中至少有一個沒滿足掉蔬。

? ? ? ? 前提:

? ? ? ? 1,必須是兩個及兩個以上的線程查近。滿足了眉踱。

? ? ? ? 2,用的是同一個鎖霜威。不是谈喳。0線程用的obj鎖,1線程用的this鎖戈泼。

? ? ? ? 我們將同步代碼塊也改成this鎖試一試:?

? ? ? ? 編譯運(yùn)行:

? ? ? ? 安全啦婿禽!

? ? ? ? 通過這個小程序,我們也側(cè)面驗(yàn)證了同步函數(shù)使用的鎖是this大猛,一箭雙雕嘻嘻~

????13-多線程(多線程-靜態(tài)同步函數(shù)的鎖是Class對象)

? ? ? ? 我們將show用static修飾:

? ? ? ? tick也用static修飾:

? ? ? ? 編譯運(yùn)行:

? ? ? ? 我們發(fā)現(xiàn)扭倾,又出現(xiàn)0號票了,怎么會醬紫挽绩!

? ? ? ? 因此膛壹,靜態(tài)的同步函數(shù),用的鎖肯定不是this了(因?yàn)殪o態(tài)方法中也不可以定義this)唉堪,那么它用的鎖是什么呢模聋?

? ? ? ? 靜態(tài)進(jìn)內(nèi)存時,內(nèi)存中沒有本類對象唠亚,到那時一定有該類對應(yīng)的字節(jié)碼文件對象:類名.class链方,該對象的類型是Class。

? ? ? ? 所以將同步代碼塊的鎖修改成Ticket.class試一下:

? ? ? ? 編譯運(yùn)行:

? ? ? ? 安全啦灶搜!

? ? ? ? 因此祟蚀,靜態(tài)的同步方法工窍,使用的鎖是該方法所在類的字節(jié)碼文件對象:類名.class。

? ??14-多線程(多線程-單例設(shè)計(jì)模式-懶漢式)

? ? ? ? 講完了這些前酿,我們回過頭看一下之前講過的單例設(shè)計(jì)模式——懶漢式患雏。因?yàn)檫@個小知識點(diǎn)只有我們學(xué)完同步之后才能講~? ?

? ? ? ? 我們回顧一下單例設(shè)計(jì)模式:

? ? ? ? 餓漢式:

? ? ? ? 懶漢式:

? ? ? ? 這里有一個問題,如果多個線程并發(fā)訪問這個getInstance薪者,是不是有多條語句在操作共享數(shù)據(jù)s纵苛?(一條在判斷,一條在賦值言津。)

? ? ? ? 加上synchronized就搞定了:

? ? ? ? 但是還有個問題攻人,如果有很多個線程都要訪問getInstance,是不是每個線程都要訪問這個鎖悬槽?所以懶漢式加了同步會比較低效怀吻。

? ? ? ? 怎么辦呢?

? ? ? ? 這個時候我們可以這樣干:

? ? ? ? 但是這樣寫和那樣寫不是沒啥區(qū)別嘛初婆,那再加一句:

? ? ? ? 我們分析一下:

? ? ? ? A一進(jìn)來蓬坡,滿足第一次s==null判斷,拿到鎖就進(jìn)來了磅叛,進(jìn)來之后A掛這里了:

? ? ? ? B一進(jìn)來屑咳,也滿足第一次s==null判斷,但是沒拿到鎖弊琴。

? ? ? ? 這個時候A繼續(xù)執(zhí)行s=new Single();并且在執(zhí)行完之后就出去啦兆龙。

? ? ? ? 這時候B拿到了鎖,也進(jìn)來了敲董。進(jìn)行第二次s==null的判斷紫皇,顯然不滿足。所以return s腋寨。

? ? ? ? 這時C進(jìn)來了聪铺,進(jìn)行第一次s==null的判斷,不滿足萄窜。而且C以后進(jìn)來的線程铃剔,它們都不滿足第一次s==null的判斷,所以都不用再判斷這個鎖查刻。

? ? ? ? 這樣做番宁,減少了判斷鎖的次數(shù)。

? ? ? ? 所以用雙重判斷赖阻,減少了判斷鎖的次數(shù),稍微提高了懶漢式的效率踱蠢。

? ? ? ? 但是不管怎樣火欧,餓漢式只有一句話就可以解決的問題棋电,懶漢式都要用好幾句。所以開發(fā)中還是寫?zhàn)I漢式好一些苇侵。

? ? ? ? 之所以講這么詳細(xì)赶盔,是因?yàn)樵诿嬖囍薪?jīng)常會考到懶漢式。

????????比如餓漢式和懶漢式有什么不同榆浓?懶漢式的特點(diǎn)在于實(shí)例的延遲加載于未。

? ? ? ? 懶漢式的延遲加載有沒有問題?有陡鹃, 如果多線程訪問時會出現(xiàn)安全問題烘浦。?

? ? ? ? 怎么解決?用同步來解決萍鲸。用同步代碼塊或者同步函數(shù)都可以闷叉,但是稍微有點(diǎn)低效。用雙重判斷可以稍微解決一下效率問題脊阴。

? ? ? ? 加同步的時候使用的鎖是哪一個握侧?該類所處的字節(jié)碼對象。

? ? ? ? 所以嘿期,就這一個小問題可以考好多地方呢品擎。

? ? ? ? 這個代碼要會寫哦:

? ? ? ? 一般有可能會要求:請寫一個延遲加載的單例設(shè)計(jì)模式示例。就寫這個~

????15-多線程(多線程-死鎖)

? ? ? ? 剛剛講的this鎖和字節(jié)碼對象鎖結(jié)論記住就行啦备徐,代碼有點(diǎn)麻煩萄传,了解即可。但是單例設(shè)計(jì)模式的代碼一定要會哦坦喘。? ? ? ??

? ? ? ? 回到我們的進(jìn)度中來盲再。

? ? ? ? 同步還有一個弊端:死鎖。這是同步出現(xiàn)之后會產(chǎn)生的一個現(xiàn)象瓣铣。

? ? ? ? 何為死鎖呢答朋?

? ? ? ? 你持有一個鎖,我也持有一個鎖棠笑,我不放我的鎖要到你那里面去運(yùn)行梦碗,而你不放你的鎖要到我這里面去運(yùn)行。誰都不放蓖救,就導(dǎo)致了死鎖洪规。

? ? ? ? 死鎖一產(chǎn)生,程序就掛那兒不動了循捺。

? ? ? ? 通常死鎖出現(xiàn)的原因是:同步中嵌套出現(xiàn)斩例,而鎖卻不同。

? ? ? ? 我們稍微修改一下代碼从橘,讓它出現(xiàn)這種情況:

? ? ? ? 編譯運(yùn)行念赶,卡這里不動了:

? ? ? ? ?我們分析一下:?

? ? ? ? 這個時候就鎖住了础钠。

? ? ? ? 注意這樣寫不是教你寫死鎖哦,是要你一定要避免死鎖~

? ? ? ? 再來一個稍微簡單的死鎖例子:

? ? ? ? Test類實(shí)現(xiàn)Runnable接口:

????????為了方便起見叉谜,單獨(dú)寫了一個類裝倆鎖(鎖是對象哦):

????????run方法中:

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

? ? ? ? 編譯運(yùn)行:

? ? ? ? 鎖住了旗吁。

? ? ? ? 分析原因:

? ? ? ? 當(dāng)然多運(yùn)行幾次偶爾也會出現(xiàn)和諧的情況。(但是這只是僥幸M>帧)

? ? ? ? 面試的時候很钓,有一道考題叫:請給我寫一個死鎖程序。這個考的就是對死鎖的理解董栽。能寫出來码倦,就說明對死鎖理解的差不多了,避免死鎖也不是問題了裆泳。

? ? ? ? 終于寫完啦叹洲,吃午餐去辣!想吃面面~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末工禾,一起剝皮案震驚了整個濱河市运提,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌闻葵,老刑警劉巖民泵,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異槽畔,居然都是意外死亡栈妆,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進(jìn)店門厢钧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鳞尔,“玉大人,你說我怎么就攤上這事早直×燃伲” “怎么了?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵霞扬,是天一觀的道長糕韧。 經(jīng)常有香客問我,道長喻圃,這世上最難降的妖魔是什么萤彩? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮斧拍,結(jié)果婚禮上雀扶,老公的妹妹穿的比我還像新娘。我一直安慰自己肆汹,他們只是感情好愚墓,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布窍侧。 她就那樣靜靜地躺著,像睡著了一般转绷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上硼啤,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天议经,我揣著相機(jī)與錄音,去河邊找鬼谴返。 笑死煞肾,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嗓袱。 我是一名探鬼主播籍救,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼渠抹!你這毒婦竟也來了蝙昙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤梧却,失蹤者是張志新(化名)和其女友劉穎奇颠,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體放航,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡烈拒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了广鳍。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荆几。...
    茶點(diǎn)故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖赊时,靈堂內(nèi)的尸體忽然破棺而出吨铸,到底是詐尸還是另有隱情,我是刑警寧澤蛋叼,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布焊傅,位于F島的核電站,受9級特大地震影響狈涮,放射性物質(zhì)發(fā)生泄漏狐胎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一歌馍、第九天 我趴在偏房一處隱蔽的房頂上張望握巢。 院中可真熱鬧,春花似錦松却、人聲如沸暴浦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽歌焦。三九已至飞几,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間独撇,已是汗流浹背屑墨。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留纷铣,地道東北人卵史。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像搜立,于是被迫代替她去往敵國和親以躯。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評論 2 349

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