淺談 Java 多線程

? ? ? ? 首先显沈,我們來整體看一下線程是什么?從操作系統(tǒng)的角度逢唤,可以簡(jiǎn)單認(rèn)為拉讯,線程是系統(tǒng)調(diào)度的最小單元,一個(gè)進(jìn)程可以包含多個(gè)線程鳖藕,作為任務(wù)的真正運(yùn)作者魔慷,有自己的棧,寄存器著恩,本地存儲(chǔ)等院尔。但是會(huì)和進(jìn)程內(nèi)其他線程共享文件描述符蜻展,虛擬地址空間等。在具體實(shí)現(xiàn)中邀摆,線程還分為內(nèi)核線程纵顾,用戶線程,Java 的線程實(shí)現(xiàn)其實(shí)是與虛擬機(jī)相關(guān)的栋盹。

第一施逾,一個(gè)線程可以兩次調(diào)用 start()方法嗎?

? ? ? ? Java 的線程是不允許啟動(dòng)例获,第二次調(diào)用必然會(huì)拋出 IllegalThreadStateException汉额,這是一種運(yùn)行時(shí)異常,多次調(diào)用 start 被認(rèn)為是編程錯(cuò)誤榨汤。

? ? ? ? 關(guān)于線程生命周期的不同狀態(tài)蠕搜,在 Java 5 以后,線程狀態(tài)被明確定義在其公共內(nèi)部枚舉類型 java.lang.Thread.State 中收壕,分別是:

? ? ? ? 新建(NEW)讥脐,表示線程被創(chuàng)建出來還沒真正啟動(dòng)的狀態(tài),可以認(rèn)為它是個(gè) Java 內(nèi)部狀態(tài)啼器。

? ? ? ? 就緒(RUNNABLE),表示該線程已經(jīng)在 JVM 中執(zhí)行,當(dāng)然由于執(zhí)行需要計(jì)算資源俱萍,它可能是正在運(yùn)行端壳,也可能還在等待系統(tǒng)分配給它 CPU 片段,在就緒隊(duì)列里面排隊(duì)枪蘑。在其它一些分析中损谦,會(huì)額外區(qū)分一種狀態(tài) RUNNING,但是從 JAVA API 的角度岳颇,并不能表示出來照捡。

? ? ? ? 阻塞(BLOCKED),這個(gè)狀態(tài)和我們前面兩講介紹的同步非常相關(guān)话侧,阻塞表示線程在等待 Monitor lock栗精。比如,線程試圖通過 synchronized 去獲取某個(gè)鎖瞻鹏,但是其它線程已經(jīng)獨(dú)占了悲立,那么當(dāng)前線程就會(huì)處于阻塞狀態(tài),或者線程調(diào)用 sleep() 方法進(jìn)入阻塞狀態(tài)新博。yield()? 方法是一個(gè)和 sleep() 方法有點(diǎn)相似的方法薪夕,它也是 Thread 類提供的一個(gè)靜態(tài)方法『涨模可以讓當(dāng)前正在執(zhí)行的線程暫停原献,但它不會(huì)阻塞該線程馏慨,只是將該線程轉(zhuǎn)入就緒狀態(tài)。yeild() 只是讓當(dāng)前線程暫停一下姑隅,讓系統(tǒng)的線程調(diào)度器重新調(diào)度一次写隶,完全可能的情況是:當(dāng)某個(gè)線程調(diào)用了 yield() 線程暫停之后,線程調(diào)度器又將其調(diào)度出來重新執(zhí)行粤策。

? ? ? ? 等待(WAITING)樟澜,表示正在等待其它線程采取某些操作。一個(gè)常見的場(chǎng)景是類似生產(chǎn)者消費(fèi)者模式叮盘,發(fā)現(xiàn)任務(wù)條件尚未滿足秩贰,就讓當(dāng)前消費(fèi)者等待(wait),另外的生產(chǎn)者線程去準(zhǔn)備任務(wù)數(shù)據(jù)柔吼,然后通過類似 notify 等動(dòng)作毒费,通知消費(fèi)線程可以繼續(xù)工作了。Thread.join()也會(huì)令線程進(jìn)入等待狀態(tài)愈魏。解釋一下 Thread.join()觅玻,是主線程等待子線程的終止。也就是說主線程的代碼塊中培漏,如果碰到了 t.join() (t 是子線程)方法溪厘,此時(shí)主線程需要等待(阻塞),等待子線程結(jié)束了 (Waits for this thread to die.) 牌柄,才能繼續(xù)執(zhí)行 t.join() 之后的代碼塊畸悬。join()和?wait()不會(huì)釋放鎖,join()是 Thread 的方法珊佣,wait()是 Object 的方法蹋宦。

? ? ? ? 計(jì)時(shí)等待(TIMED_WAIT),其進(jìn)入條件和等待狀態(tài)類似咒锻,但是調(diào)用的是存在超時(shí)條件的方法冷冗,比如 wait 和 join 等方法的指定超時(shí)版本。

? ? ? ? 終止(TERMINATED)惑艇,不管是意外退出還是正常執(zhí)行結(jié)束蒿辙,線程已經(jīng)完成使命,終止運(yùn)行敦捧,也有人把這個(gè)狀態(tài)叫做死亡须板。

? ? ? ? 第二次調(diào)用 start()方法的時(shí)候,線程可能處于終止或者其他(非 NEW)狀態(tài)兢卵,但是不論如何习瑰,都不是可以啟動(dòng)的。

第二秽荤,什么情況下 Java 程序會(huì)產(chǎn)生死鎖甜奄?如何定位柠横,修復(fù)?

? ? ? ? 死鎖是一種特定的程序狀態(tài)课兄,在實(shí)體之間牍氛,由于循環(huán)依賴導(dǎo)致彼此一直處于等待之中,沒有任何個(gè)體可以繼續(xù)前進(jìn)烟阐。死鎖不僅僅是在線程之間會(huì)發(fā)生搬俊,存在資源獨(dú)占的進(jìn)程之間同樣也可能出現(xiàn)死鎖。通常來說蜒茄,我們大多是聚焦在多線程場(chǎng)景種的死鎖唉擂,指兩個(gè)或多個(gè)線程之間,由于互相持有對(duì)方需要的鎖檀葛,而永久處于阻塞的狀態(tài)玩祟。如下圖:

????????定位死鎖最常見的方式就是利用 jstack 等工具獲取線程棧,然后定位互相之間的依賴關(guān)系屿聋,進(jìn)而找到死鎖空扎。如果是比較明顯的死鎖,往往 jstack 等就能直接定位润讥,類似 JConsole 甚至可以在圖形界面進(jìn)行有限的死鎖檢測(cè)转锈。如果程序運(yùn)行時(shí)發(fā)生了死鎖,絕大多數(shù)情況下都是無法在線解決的楚殿,只能重啟黑忱,修正程序本身問題。所以勒魔,代碼開發(fā)階段互相審查,或者利用工具進(jìn)行預(yù)防性排查菇曲,往往也是很重要的冠绢。

第三,如何在編程中盡量預(yù)防死鎖常潮?

? ? ? ? 首先弟胀,我們來總結(jié)一下前面例子中死鎖的產(chǎn)生包含哪些基本元素『笆剑基本上死鎖的發(fā)生時(shí)因?yàn)椋?孵户,互斥條件,類似 Java 中 Monitor 都是獨(dú)占的岔留,要么是我用夏哭,要么是你用。2献联,互斥條件是長(zhǎng)期持有的竖配,在使用結(jié)束之前何址,自己不會(huì)釋放,也不能被其他線程搶占进胯。3用爪,循環(huán)依賴關(guān)系,兩個(gè)或者多個(gè)個(gè)體之間出現(xiàn)了鎖的鏈條環(huán)胁镐。所以偎血,我們可以據(jù)此分析可能的避免死鎖的思路和方法。

? ? ? ? 第一種方法:盡量避免使用多個(gè)鎖盯漂,并且只有需要時(shí)才持有鎖颇玷。否則,即使是非常精通并發(fā)編程的工程師宠能,也難避免會(huì)掉進(jìn)坑里亚隙,嵌套的 synchronized 或者 lock 非常容易出問題。所以违崇,從程序設(shè)計(jì)的角度反思阿弃,如果我們賦予一段程序太多的職責(zé),出現(xiàn) “既要羞延。渣淳。又要。伴箩∪肜ⅲ” 的情況時(shí),可能就需要我們審視下設(shè)計(jì)思路或目的是否合理了嗤谚。對(duì)于類庫(kù)棺蛛,因?yàn)槠浠A(chǔ),共享的定位巩步,比應(yīng)用開發(fā)往往更加令人苦惱旁赊,需要仔細(xì)斟酌之間的平衡。

? ? ? ? 第二種方法:如果必須使用多個(gè)鎖椅野,經(jīng)量設(shè)計(jì)好鎖的獲取順序终畅,這個(gè)說起來簡(jiǎn)單,做起來可不容易竟闪,可以參看著名的銀行家算法离福。

? ? ? ? 第三種方法:使用帶超時(shí)的方法,為程序帶來更多可控性炼蛤。類似 Object.wait(...) 或者 CountDownLatch.await(...)妖爷,都支持所謂的 timed_wait,我們完全可以就不假定該鎖一定會(huì)獲得理朋,指定超時(shí)時(shí)間赠涮,并為無法得到鎖時(shí)準(zhǔn)備退出邏輯子寓。并發(fā) Lock 實(shí)現(xiàn),如 ReentrantLock 還支持非阻塞式的獲取鎖操作 tryLock()笋除,這是一個(gè)插隊(duì)行為斜友,并不在乎等待的公平性,如果執(zhí)行時(shí)對(duì)象恰好沒有被獨(dú)占垃它,則直接獲取鎖鲜屏。

? ? ? ? 第四種方法:通過靜態(tài)代碼分析去查找固定的模式,進(jìn)而定位可能的死鎖或者競(jìng)爭(zhēng)情況国拇。?死鎖的另一個(gè)好朋友就是饑餓洛史。死鎖和饑餓都是線程活躍性問題。實(shí)踐中死鎖可以使用 jvm 自帶的工具進(jìn)行排查酱吝。死循環(huán)死鎖可以認(rèn)為是自旋鎖死鎖的一種也殖,其他線程因?yàn)榈却坏骄唧w的信號(hào)提示。導(dǎo)致線程一直饑餓务热。這種情況下可以查看線程 cpu 使用情況忆嗜,排查出使用 cpu 時(shí)間片最高的線程,再打出該線程的堆棧信息崎岂,排查代碼捆毫。基于互斥量的鎖如果發(fā)生死鎖往往 cpu 使用率較低冲甘,實(shí)踐中也可以從這一方面進(jìn)行排查绩卤。

我是溫馭臣,一個(gè)java的開發(fā)學(xué)習(xí)者江醇,以上是我的簡(jiǎn)單總結(jié)濒憋,如果有缺陷,希望在評(píng)論區(qū)看到您的補(bǔ)充陶夜。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末跋炕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子律适,更是在濱河造成了極大的恐慌,老刑警劉巖遏插,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捂贿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡胳嘲,警方通過查閱死者的電腦和手機(jī)厂僧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來了牛,“玉大人颜屠,你說我怎么就攤上這事辰妙。” “怎么了甫窟?”我有些...
    開封第一講書人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵密浑,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我粗井,道長(zhǎng)尔破,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任浇衬,我火速辦了婚禮懒构,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘耘擂。我一直安慰自己胆剧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開白布醉冤。 她就那樣靜靜地躺著秩霍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪冤灾。 梳的紋絲不亂的頭發(fā)上前域,一...
    開封第一講書人閱讀 51,245評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音韵吨,去河邊找鬼匿垄。 笑死,一個(gè)胖子當(dāng)著我的面吹牛归粉,可吹牛的內(nèi)容都是我干的椿疗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼糠悼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼届榄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起倔喂,我...
    開封第一講書人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤铝条,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后席噩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體班缰,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年悼枢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了埠忘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖莹妒,靈堂內(nèi)的尸體忽然破棺而出名船,到底是詐尸還是另有隱情,我是刑警寧澤旨怠,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布渠驼,位于F島的核電站,受9級(jí)特大地震影響运吓,放射性物質(zhì)發(fā)生泄漏渴邦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一拘哨、第九天 我趴在偏房一處隱蔽的房頂上張望谋梭。 院中可真熱鬧,春花似錦倦青、人聲如沸瓮床。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)隘庄。三九已至,卻和暖如春癣亚,著一層夾襖步出監(jiān)牢的瞬間丑掺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工述雾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留街州,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓玻孟,卻偏偏與公主長(zhǎng)得像唆缴,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子黍翎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

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

  • 一群‘’年輕人‘’學(xué)習(xí)藍(lán)染 找到家里的舊布料 扎成心目中的樣子 投到染缸里面徽,期待著 期待嫵媚的花瓣 期待流暢的山巒...
    走近白玉蘭閱讀 901評(píng)論 11 9
  • 2017年11月,美國(guó)心臟協(xié)會(huì)和美國(guó)心臟病學(xué)會(huì)改變了高血壓的定義匣掸。 趟紊、某天,你的血壓是130/80這是正常的數(shù)值 ...
    ldxyq閱讀 377評(píng)論 0 0
  • 昨天晚上碰酝,繼續(xù)觀看了中央電視臺(tái)大型詩(shī)詞文化音樂節(jié)目《經(jīng)典詠流傳》霎匈,節(jié)目響應(yīng)落實(shí)十九大報(bào)告“推動(dòng)中華優(yōu)秀傳統(tǒng)文化創(chuàng)造...
    三寶媽肖雪強(qiáng)閱讀 8,190評(píng)論 0 1
  • 今年4月14日碱璃,上海第一家京東閃存店——“歡樂空間”在南京西路揭幕弄痹。這是半個(gè)月前的第二站,當(dāng)時(shí)一家閃存店首次出現(xiàn)在...
    夢(mèng)想在清晨閱讀 220評(píng)論 0 1
  • 以前對(duì)戀情的期待嵌器,對(duì)男人的向往如漂泊中對(duì)一個(gè)港灣的向往般肛真。 如沉睡的灰姑娘渴望王子的吻,如灰姑娘渴望水晶鞋童話的天...
    helen1990_閱讀 617評(píng)論 0 0