JAVA多線程并發(fā)之線程實(shí)現(xiàn)炎功,4種線程池凤薛,終止線程4種方式

一. JAVA 并發(fā)知識庫

二. JAVA 線程實(shí)現(xiàn)/創(chuàng)建方式

1. 繼承 Thread 類

Thread 類本質(zhì)上是實(shí)現(xiàn)了 Runnable 接口的一個(gè)實(shí)例姓建,代表一個(gè)線程的實(shí)例。啟動線程的唯一方 法就是通過 Thread 類的 start()實(shí)例方法缤苫。start()方法是一個(gè) native 方法速兔,它將啟動一個(gè)新線 程,并執(zhí)行 run()方法活玲。

2. 實(shí)現(xiàn) Runnable 接口涣狗。

如果自己的類已經(jīng) extends 另一個(gè)類,就無法直接 extends Thread舒憾,此時(shí)镀钓,可以實(shí)現(xiàn)一個(gè)

Runnable 接口。

3. ExecutorService镀迂、Callable<Class>掸宛、Future 有返回值線程

有返回值的任務(wù)必須實(shí)現(xiàn) Callable 接口,類似的招拙,無返回值的任務(wù)必須 Runnable 接口唧瘾。執(zhí)行 Callable 任務(wù)后措译,可以獲取一個(gè) Future 的對象,在該對象上調(diào)用 get 就可以獲取到 Callable 任務(wù) 返回的 Object 了饰序,再結(jié)合線程池接口 ExecutorService 就可以實(shí)現(xiàn)傳說中有返回結(jié)果的多線程 了领虹。

4. 基于線程池的方式

線程和數(shù)據(jù)庫連接這些資源都是非常寶貴的資源。那么每次需要的時(shí)候創(chuàng)建求豫,不需要的時(shí)候銷 毀塌衰,是非常浪費(fèi)資源的。那么我們就可以使用緩存的策略蝠嘉,也就是使用線程池最疆。

三. 種線程池

Java 里面線程池的頂級接口是?Executor,但是嚴(yán)格意義上講 Executor 并不是一個(gè)線程池蚤告,而 只是一個(gè)執(zhí)行線程的工具努酸。真正的線程池接口是?ExecutorService

1. newCachedThreadPool

創(chuàng)建一個(gè)可根據(jù)需要創(chuàng)建新線程的線程池杜恰,但是在以前構(gòu)造的線程可用時(shí)將重用它們获诈。對于執(zhí)行 很多短期異步任務(wù)的程序而言,這些線程池通承暮郑可提高程序性能舔涎。調(diào)用 execute 將重用以前構(gòu)造的線程(如果線程可用)。如果現(xiàn)有線程沒有可用的逗爹,則創(chuàng)建一個(gè)新線程并添加到池中亡嫌。終止并從緩存中移除那些已有 60 秒鐘未被使用的線程。因此掘而,長時(shí)間保持空閑的線程池不會使用任何資源昼伴。

2. newFixedThreadPool

創(chuàng)建一個(gè)可重用固定線程數(shù)的線程池,以共享的無界隊(duì)列方式來運(yùn)行這些線程镣屹。在任意點(diǎn)圃郊,在大多數(shù) nThreads 線程會處于處理任務(wù)的活動狀態(tài)。如果在所有線程處于活動狀態(tài)時(shí)提交附加任務(wù)女蜈,則在有可用線程之前,附加任務(wù)將在隊(duì)列中等待逸寓。如果在關(guān)閉前的執(zhí)行期間由于失敗而導(dǎo)致任何線程終止覆山,那么一個(gè)新線程將代替它執(zhí)行后續(xù)的任務(wù)(如果需要)竹伸。在某個(gè)線程被顯式地關(guān)閉之前,池中的線程將一直存在。

3. newScheduledThreadPool

創(chuàng)建一個(gè)線程池勋篓,它可安排在給定延遲后運(yùn)行命令或者定期地執(zhí)行吧享。

4. newSingleThreadExecutor

Executors.newSingleThreadExecutor()返回一個(gè)線程池(這個(gè)線程池只有一個(gè)線程),這個(gè)線程 池可以在線程死后(或發(fā)生異常時(shí))重新啟動一個(gè)線程來替代原來的線程繼續(xù)執(zhí)行下去!

四. 線程生命周期(狀態(tài))

當(dāng)線程被創(chuàng)建并啟動以后譬嚣,它既不是一啟動就進(jìn)入了執(zhí)行狀態(tài)钢颂,也不是一直處于執(zhí)行狀態(tài)。 在線程的生命周期中拜银,它要經(jīng)過新建(New)、就緒(Runnable)尼桶、運(yùn)行(Running)、阻塞 (Blocked)和死亡(Dead)5 種狀態(tài)趾盐。尤其是當(dāng)線程啟動以后,它不可能一直"霸占"著 CPU 獨(dú)自 運(yùn)行谤碳,所以 CPU 需要在多條線程之間切換溢豆,于是線程狀態(tài)也會多次在運(yùn)行、阻塞之間切換

1. 新建狀態(tài)(NEW)

當(dāng)程序使用 new 關(guān)鍵字創(chuàng)建了一個(gè)線程之后瘸羡,該線程就處于新建狀態(tài)漩仙,此時(shí)僅由 JVM 為其分配內(nèi)存队他,并初始化其成員變量的值

2. 就緒狀態(tài)(RUNNABLE):

當(dāng)線程對象調(diào)用了 start()方法之后粘昨,該線程處于就緒狀態(tài)。Java 虛擬機(jī)會為其創(chuàng)建方法調(diào)用棧和程序計(jì)數(shù)器馁启,等待調(diào)度運(yùn)行。

3. 運(yùn)行狀態(tài)(RUNNING):

如果處于就緒狀態(tài)的線程獲得了 CPU,開始執(zhí)行 run()方法的線程執(zhí)行體,則該線程處于運(yùn)行狀態(tài)。

4. 阻塞狀態(tài)(BLOCKED):

阻塞狀態(tài)是指線程因?yàn)槟撤N原因放棄了 cpu 使用權(quán),也即讓出了 cpu timeslice歹袁,暫時(shí)停止運(yùn)行。直到線程進(jìn)入可運(yùn)行(runnable)狀態(tài)寝优,才有機(jī)會再次獲得 cpu timeslice 轉(zhuǎn)到運(yùn)行(running)狀態(tài)乏矾。阻塞的情況分三種:

等待阻塞(o.wait->等待對列):

運(yùn)行(running)的線程執(zhí)行 o.wait()方法,JVM 會把該線程放入等待隊(duì)列(waitting queue) 中钻心。

同步阻塞(lock->鎖池)

運(yùn)行(running)的線程在獲取對象的同步鎖時(shí)凄硼,若該同步鎖被別的線程占用捷沸,則 JVM 會把該線 程放入鎖池(lock pool)中。

其他阻塞(sleep/join)

運(yùn)行(running)的線程執(zhí)行 Thread.sleep(long ms)或 t.join()方法痒给,或者發(fā)出了 I/O 請求時(shí)说墨, JVM 會把該線程置為阻塞狀態(tài)婉刀。當(dāng) sleep()狀態(tài)超時(shí)、join()等待線程終止或者超時(shí)序仙、或者 I/O 處理完畢時(shí)突颊,線程重新轉(zhuǎn)入可運(yùn)行(runnable)狀態(tài)。

5. 線程死亡(DEAD)

線程會以下面三種方式結(jié)束,結(jié)束后就是死亡狀態(tài)律秃。

正常結(jié)束

1. run()或 call()方法執(zhí)行完成爬橡,線程正常結(jié)束。

異常結(jié)束

2. 線程拋出一個(gè)未捕獲的 Exception 或 Error棒动。

調(diào)用 stop

3. 直接調(diào)用該線程的 stop()方法來結(jié)束該線程—該方法通常容易導(dǎo)致死鎖糙申,不推薦使用。

五. 終止線程 4 種方式

1. 正常運(yùn)行結(jié)束

程序運(yùn)行結(jié)束船惨,線程自動結(jié)束柜裸。

2. 使用退出標(biāo)志退出線程

一般 run()方法執(zhí)行完,線程就會正常結(jié)束粱锐,然而疙挺,常常有些線程是伺服線程。它們需要長時(shí)間的運(yùn)行怜浅,只有在外部某些條件滿足的情況下铐然,才能關(guān)閉這些線程。使用一個(gè)變量來控制循環(huán)恶座,例如:最直接的方法就是設(shè)一個(gè) boolean 類型的標(biāo)志搀暑,并通過設(shè)置這個(gè)標(biāo)志為 true 或 false 來控制 while循環(huán)是否退出,代碼示例:

定義了一個(gè)退出標(biāo)志 exit跨琳,當(dāng) exit 為 true 時(shí)自点,while 循環(huán)退出,exit 的默認(rèn)值為 false.在定義 exit時(shí)湾宙,使用了一個(gè) Java 關(guān)鍵字 volatile樟氢,這個(gè)關(guān)鍵字的目的是使 exit 同步冈绊,也就是說在同一時(shí)刻只能由一個(gè)線程來修改 exit 的值侠鳄。

3. Interrupt 方法結(jié)束線程

使用 interrupt()方法來中斷線程有兩種情況:

1. 線程處于阻塞狀態(tài):如使用了 sleep,同步鎖的 wait,socket 中的 receiver,accept 等方法時(shí),會使線程處于阻塞狀態(tài)死宣。當(dāng)調(diào)用線程的 interrupt()方法時(shí)伟恶,會拋出 InterruptException 異常。阻塞中的那個(gè)方法拋出這個(gè)異常毅该,通過代碼捕獲該異常博秫,然后 break 跳出循環(huán)狀態(tài),從而讓我們有機(jī)會結(jié)束這個(gè)線程的執(zhí)行眶掌。通常很多人認(rèn)為只要調(diào)用 interrupt 方法線程就會結(jié)束挡育,實(shí)際上是錯的, 一定要先捕獲 InterruptedException 異常之后通過 break 來跳出循環(huán)朴爬,才能正常結(jié)束 run 方法即寒。

2. 線程未處于阻塞狀態(tài):使用 isInterrupted()判斷線程的中斷標(biāo)志來退出循環(huán)。當(dāng)使用 interrupt()方法時(shí),中斷標(biāo)志就會置 true母赵,和使用自定義的標(biāo)志來控制循環(huán)是一樣的道理逸爵。

4. stop 方法終止線程(線程不安全)

程序中可以直接使用 thread.stop()來強(qiáng)行終止線程,但是 stop 方法是很危險(xiǎn)的凹嘲,就象突然關(guān) 閉計(jì)算機(jī)電源师倔,而不是按正常程序關(guān)機(jī)一樣,可能會產(chǎn)生不可預(yù)料的結(jié)果周蹭,不安全主要是: thread.stop()調(diào)用之后趋艘,創(chuàng)建子線程的線程就會拋出 ThreadDeatherror 的錯誤,并且會釋放子 線程所持有的所有鎖凶朗。一般任何進(jìn)行加鎖的代碼塊致稀,都是為了保護(hù)數(shù)據(jù)的一致性,如果在調(diào)用 thread.stop()后導(dǎo)致了該線程所持有的所有鎖的突然釋放(不可控制)俱尼,那么被保護(hù)數(shù)據(jù)就有可能呈 現(xiàn)不一致性抖单,其他線程在使用這些被破壞的數(shù)據(jù)時(shí),有可能導(dǎo)致一些很奇怪的應(yīng)用程序錯誤遇八。因此矛绘,并不推薦使用 stop 方法來終止線程。

5. sleep 與 wait 區(qū)別

(1). 對于 sleep()方法刃永,我們首先要知道該方法是屬于 Thread 類中的货矮。而 wait()方法,則是屬于Object 類中的斯够。13/04/2018 Page 62 of 283

(2). sleep()方法導(dǎo)致了程序暫停執(zhí)行指定的時(shí)間囚玫,讓出 cpu 該其他線程,但是他的監(jiān)控狀態(tài)依然保持者读规,當(dāng)指定的時(shí)間到了又會自動恢復(fù)運(yùn)行狀態(tài)抓督。

(3). 在調(diào)用 sleep()方法的過程中,線程不會釋放對象鎖束亏。

(4). 而當(dāng)調(diào)用 wait()方法的時(shí)候铃在,線程會放棄對象鎖,進(jìn)入等待此對象的等待鎖定池碍遍,只有針對此對象調(diào)用 notify()方法后本線程才進(jìn)入對象鎖定池準(zhǔn)備獲取對象鎖進(jìn)入運(yùn)行狀態(tài)定铜。

6. start 與 run 區(qū)別

(1). start()方法來啟動線程,真正實(shí)現(xiàn)了多線程運(yùn)行怕敬。這時(shí)無需等待 run 方法體代碼執(zhí)行完畢揣炕,可以直接繼續(xù)執(zhí)行下面的代碼。

(2). 通過調(diào)用 Thread 類的 start()方法來啟動一個(gè)線程东跪, 這時(shí)此線程是處于就緒狀態(tài)畸陡, 并沒有運(yùn)行矮烹。

(3). 方法 run()稱為線程體,它包含了要執(zhí)行的這個(gè)線程的內(nèi)容罩锐,線程就進(jìn)入了運(yùn)行狀態(tài)奉狈,開始運(yùn)行 run 函數(shù)當(dāng)中的代碼。 Run 方法運(yùn)行結(jié)束涩惑, 此線程終止仁期。然后 CPU 再調(diào)度其它線程。

歡迎大家點(diǎn)贊關(guān)注 轉(zhuǎn)發(fā)評論一起來討論竭恬。會每天給大家?guī)硪坏絻蓚€(gè)知識點(diǎn)跛蛋,一起成長

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市痊硕,隨后出現(xiàn)的幾起案子赊级,更是在濱河造成了極大的恐慌,老刑警劉巖岔绸,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件理逊,死亡現(xiàn)場離奇詭異,居然都是意外死亡盒揉,警方通過查閱死者的電腦和手機(jī)晋被,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刚盈,“玉大人羡洛,你說我怎么就攤上這事∨菏” “怎么了欲侮?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵违寞,是天一觀的道長搁胆。 經(jīng)常有香客問我庵佣,道長钞澳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任惨远,我火速辦了婚禮奶段,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘沙兰。我一直安慰自己,他們只是感情好翘魄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布鼎天。 她就那樣靜靜地躺著,像睡著了一般暑竟。 火紅的嫁衣襯著肌膚如雪斋射。 梳的紋絲不亂的頭發(fā)上育勺,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天,我揣著相機(jī)與錄音罗岖,去河邊找鬼涧至。 笑死,一個(gè)胖子當(dāng)著我的面吹牛桑包,可吹牛的內(nèi)容都是我干的南蓬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼哑了,長吁一口氣:“原來是場噩夢啊……” “哼赘方!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起弱左,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤窄陡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后拆火,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體跳夭,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年们镜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了优妙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,769評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡憎账,死狀恐怖套硼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情胞皱,我是刑警寧澤邪意,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站反砌,受9級特大地震影響雾鬼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜宴树,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一策菜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧酒贬,春花似錦又憨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至零如,卻和暖如春躏将,著一層夾襖步出監(jiān)牢的瞬間锄弱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工祸憋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留会宪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓蚯窥,卻偏偏與公主長得像狈谊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子沟沙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評論 2 354

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