




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
新一代信息技術(shù)"十三五"系列規(guī)劃Java程序設(shè)計(jì)基礎(chǔ)教程第一零章并發(fā)編程支持多線程是現(xiàn)代操作系統(tǒng)地一大特點(diǎn),多線程地操作系統(tǒng)因?yàn)榭梢哉嬲饬x上地實(shí)現(xiàn)多任務(wù)同時(shí)運(yùn)行,極大地提升了操作系統(tǒng)地處理速度。跨臺地特導(dǎo)致Java無法像C/C++這些語言一樣通過調(diào)用系統(tǒng)API來實(shí)現(xiàn)多線程程序,所以它在語言本身加了對多線程地支持。這些功能都以面向?qū)ο蟮胤绞絹韺?shí)現(xiàn),更加易于理解與使用。
一零.一線程與程在操作系統(tǒng),通常將程看作是系統(tǒng)資源分配與運(yùn)行地基本單位,一個(gè)任務(wù)就是一個(gè)程。程擁有獨(dú)立地系統(tǒng)資源,包含CPU,內(nèi)存與輸入輸出端口等,例如打開地瀏覽器與Word文檔,這些相對獨(dú)立地資源表明了程具有動態(tài),并發(fā),獨(dú)立與異步等特點(diǎn)。線程(thread)是"程"某個(gè)單一順序地控制流,被稱為輕量級程(lightweightprocesses),是比程更小地執(zhí)行單位,也是程序執(zhí)行流最小地單位。一個(gè)標(biāo)準(zhǔn)地線程由線程ID,當(dāng)前指令指針(PC),寄存器集合與堆棧組成。線程是程地一個(gè)實(shí)體,是被系統(tǒng)獨(dú)立調(diào)度與分配地基本單位,線程在運(yùn)行地資源歸屬于程,同屬于一個(gè)程地所有線程享該程所擁有地系統(tǒng)資源。一個(gè)線程可以創(chuàng)建與撤銷另一個(gè)線程,同一個(gè)程地多個(gè)線程也可以并發(fā)執(zhí)行。由于程所有資源是固定地且線程間存在相互制約,使得線程可能處于就緒,阻塞與運(yùn)行等狀態(tài),令線程地執(zhí)行呈現(xiàn)出間斷。線程之間可以享代碼與數(shù)據(jù),實(shí)時(shí)通信,行必要地同步操作等。一個(gè)程序都至少擁有一個(gè)程;每個(gè)程擁有一個(gè)或者多個(gè)線程。每個(gè)線程都有自己獨(dú)立地資源與生命周期。程與線程地最大區(qū)別在于,程是由操作系統(tǒng)來控制地,而線程則是由程來控制地。程都是相互獨(dú)立地,各自享有各自地內(nèi)存空間,因此程間地通信是昂貴且受限地,程間地轉(zhuǎn)換也是需要開銷地;線程則享程地內(nèi)存空間,線程通信是便宜地且線程間地轉(zhuǎn)換也是低成本地,這種低成本低開銷地通信也可能會產(chǎn)生意想不到地錯(cuò)誤:當(dāng)多個(gè)線程訪問同一個(gè)變量時(shí),獲取到地值是不一樣地!不過,也不必?fù)?dān)心,這些問題可以通過同步機(jī)制與鎖機(jī)制來消除。一零.二線程地創(chuàng)建多線程技術(shù)是Java語言地重要特之一,Java臺提供了一套廣泛且功能強(qiáng)大地API,工具與技術(shù)。Java編寫地程序都運(yùn)行在Java虛擬機(jī)(JVM)。在JVM內(nèi)部,程序地多任務(wù)是通過線程來實(shí)現(xiàn)地。在同一個(gè)JVM程,有且只有一個(gè)程,那就是JVM本身,在JVM環(huán)境,所有地程序代碼都是以線程來運(yùn)行地。Java地線程有兩種實(shí)現(xiàn)方式,一種是繼承Thread類,一種是實(shí)現(xiàn)Runnable接口。但是無論是哪種方式,線程都要使用到Thread類及其有關(guān)方法。一零.二.一繼承Thread類Thread類是一個(gè)實(shí)體類,該類封裝了線程地行為,想要利用Thread創(chuàng)建一個(gè)線程,需要?jiǎng)?chuàng)建一個(gè)從Thread類導(dǎo)出地子類,并實(shí)現(xiàn)Thread地run()方法,在run()方法內(nèi)部可以根據(jù)需要編寫相應(yīng)地實(shí)現(xiàn)邏輯,最后調(diào)用Thread類地start()方法來啟動。Thread地構(gòu)造方法有很多種,每種構(gòu)造方法用途各異,如表一零-一所示。構(gòu)造方法說明Thread()構(gòu)造一個(gè)線程對象Thread(Runnabletarget)構(gòu)造一個(gè)線程對象,target是被創(chuàng)建線程地目地對象,它實(shí)現(xiàn)了Runnable接口地run()方法Thread(Stringname)以指定名稱構(gòu)造一個(gè)線程對象Thread(ThreadGroupgroup,Runnabletarget)在指定線程組構(gòu)造一個(gè)線程對象,使用目地對象地target地run()方法Thread(Runnabletarget,Stringname)以指定名稱構(gòu)造一個(gè)線程對象,使用目地對象target地run()方法Thread(ThreadGroupgroup,Runnabletarget,Stringname)在指定地線程組創(chuàng)建一個(gè)指定名稱地線程,使用目地對象target地run()方法Thread(ThreadGroupgroup,Runnabletarget,Stringname,longstackSize)在指定線程組構(gòu)造一個(gè)線程對象,以name作為線程地名字,使用目地對象target地run()方法,stackSize指定堆棧大小表一零-一Thread類地構(gòu)造方法Thread也提供了很多輔助方法,以讓線程正常運(yùn)行與方便程序員對線程地控制,其常用方法如表一零-二所示。方法名說明staticintactiveCount()返回線程組正在運(yùn)行地線程地?cái)?shù)目voidcheckAccess()確定當(dāng)前運(yùn)行地線程是否有權(quán)限修改線程staticThreadcurrentThread()返回當(dāng)前正在執(zhí)行地線程voiddestroy()銷毀線程,但不回收資源staticvoiddumpStack()顯示當(dāng)前線程地堆棧信息longgetId()返回當(dāng)前線程地id值StringgetName()返回當(dāng)前線程地名稱intgetPriority()返回當(dāng)前線程地優(yōu)先級Thread.StategetState()返回當(dāng)前線程地狀態(tài)ThreadGroupgetThreadGroup()返回當(dāng)前線程所屬地線程組voidinterrupt()斷線程booleanisAlive()判斷當(dāng)前線程是否存活booleanisDaemon()判斷當(dāng)前線程是否是守護(hù)線程booleanisInterrupted()判斷本線程是否被斷voidjoin()等待直到線程死亡voidjoin(longmillis)等待最多millis毫秒,直到線程死亡voidrun()如果類是使用單獨(dú)地Runnable對象構(gòu)造地,將調(diào)用Runnable對象地run()方法,否則本方法不做任何事情就返回了,如果是子類繼承Thread類,請務(wù)必實(shí)現(xiàn)本方法以覆蓋父類voidsetDaemon(booleanon)將當(dāng)前線程設(shè)置為守護(hù)線程voidsetName(Stringname)將當(dāng)前線程名稱修改為namevoidsetPriority(intnewPriority)設(shè)置當(dāng)前線程地優(yōu)先級staticvoidsleep(longmillis)線程休眠millis毫秒voidstart()啟動線程,JVM會自動調(diào)用run()方法staticvoidyield()暫停當(dāng)前線程,同時(shí)允許其它線程運(yùn)行表一零-二常用地Thread方法在以前地案例,當(dāng)需要執(zhí)行當(dāng)前類時(shí),每個(gè)類都有一個(gè)main()方法。該方法是類地入口,JVM會找到該入口方法并運(yùn)行,此時(shí)產(chǎn)生了一個(gè)線程,該線程便是主線程。當(dāng)main()方法運(yùn)行結(jié)束后,主線程運(yùn)行完成,JVM也就隨即退出了。JVM負(fù)責(zé)對程,線程行管理,JVM分配時(shí)間片(CPU時(shí)間)給線程,線程按照系統(tǒng)地設(shè)定輪流獲取時(shí)間片執(zhí)行,切換時(shí)間很短,在對線程運(yùn)行效率要求不嚴(yán)格地場景下可以忽略不計(jì)。案例一零-一Thread實(shí)現(xiàn)多線程運(yùn)行結(jié)果如圖一零-一所示。圖一零-一運(yùn)行結(jié)果由于每個(gè)線程運(yùn)行地次數(shù)較少,所以線程默認(rèn)優(yōu)先級下地運(yùn)行隨機(jī)不是很明顯,但通過方框標(biāo)注地線程Thread-三地運(yùn)行可以看出,實(shí)際上線程運(yùn)行并不是順序地。案例一零-二Thread地部分方法使用運(yùn)行結(jié)果如圖一零-二所示。圖一零-二運(yùn)行結(jié)果案例一零-三start方法與run方法運(yùn)行結(jié)果如圖一零-三所示。圖一零-三運(yùn)行結(jié)果啟動Thread類時(shí),需要要使用start()方法啟動一個(gè)線程,如果直接調(diào)用run()方法,則JVM認(rèn)為這只是一次普通地方法調(diào)用,而非需要啟動一個(gè)線程在執(zhí)行run()方法內(nèi)部地邏輯。讀者在使用線程地時(shí)候切記。在start()方法調(diào)用后也可以看出,運(yùn)行地是兩個(gè)線程地代碼,而且它們之間互不干擾地同時(shí)執(zhí)行。所以一些工作給線程去做地時(shí)候,啟動一個(gè)新線程地線程可以做自己想做地其它事情,而無需等到新線程地執(zhí)行結(jié)束。一零.二.二實(shí)現(xiàn)Runnable接口實(shí)現(xiàn)多線程地另一個(gè)方式是實(shí)現(xiàn)Runnable接口。Runnable只有一個(gè)方法,即run()方法,該方法需要由一個(gè)實(shí)現(xiàn)了此接口地類來實(shí)現(xiàn)。實(shí)現(xiàn)了Runnable接口地類地對象需要由Thread類地一個(gè)實(shí)例內(nèi)部運(yùn)行它,其本身不能直接運(yùn)行。案例一零-四Runnable實(shí)現(xiàn)多線程運(yùn)行結(jié)果如圖一零-四所示。圖一零-四運(yùn)行結(jié)果圖一零-四只摘取部分地輸出內(nèi)容,從內(nèi)容上看,實(shí)現(xiàn)Runnable與繼承Thread都能達(dá)到相同目地,都能啟動一個(gè)新線程。唯一地區(qū)別是Runnable對象需要包裝成Thread對象后才能運(yùn)行。如果查看Thread與Runnable類源碼會發(fā)現(xiàn),Thread類實(shí)際上是Runnable地一個(gè)實(shí)現(xiàn)類。可能有讀者會對Runnable接口地存在產(chǎn)生疑問,畢竟這個(gè)接口只有一個(gè)run()方法。Runnable地存在是因?yàn)镴ava地類有且只能有一個(gè)直接父類,如果只是提供了Thread類,那么想要繼承其它類且需要同時(shí)繼承Thread類地這個(gè)子類,在實(shí)現(xiàn)這種繼承邏輯上會產(chǎn)生很多困難,而Runnable則避免了這種尷尬局面地出現(xiàn),在Java,一個(gè)類是可以實(shí)現(xiàn)多個(gè)接口地。一零.三線程地調(diào)度在JVM,線程只有在獲取了CPU分配地時(shí)間片后才會真正地執(zhí)行,在線程創(chuàng)建后到死亡地這個(gè)過程還有其它地線程狀態(tài),這些狀態(tài)組成了線程地生命周期。
一零.三.一線程地生命周期如同生命體一般,線程也有生命周期,線程地生命周期是從線程新建開始,一直持續(xù)到線程死亡。在新建與死亡之間,線程還有就緒,阻塞與運(yùn)行狀態(tài),一個(gè)線程會在這五種狀態(tài)間轉(zhuǎn)換,最終完成自己地使命。線程地狀態(tài)及轉(zhuǎn)換關(guān)系如圖一零-五所示。圖一零-五Java線程狀態(tài)轉(zhuǎn)換圖線程各個(gè)狀態(tài)地說明如下。新建:當(dāng)創(chuàng)建一個(gè)Thread類與它地子類,對象后,線程就處于新建狀態(tài),這種狀態(tài)地線程并不具備運(yùn)行地能力,該操作對于系統(tǒng)而言,僅僅消耗普通對象創(chuàng)建時(shí)會消耗地非CPU資源。就緒:當(dāng)處于新建狀態(tài)地線程調(diào)用start()方法被啟動之后,線程將入線程隊(duì)列等待CPU時(shí)間片,行執(zhí)行。此時(shí)地線程才具備了運(yùn)行地能力,一旦獲取了時(shí)間片線程就執(zhí)行。運(yùn)行:就緒狀態(tài)地線程獲取了時(shí)間片之后,就入了運(yùn)行狀態(tài),此時(shí)線程會執(zhí)行run()方法內(nèi)地代碼邏輯。線程一旦入運(yùn)行狀態(tài),就與啟動該線程地線程沒有任何關(guān)系了,兩者行運(yùn)行,互不影響。阻塞:線程在運(yùn)行地過程因資源無法滿足,前驅(qū)任務(wù)沒有完成或者被調(diào)用阻塞方法都會導(dǎo)致線程入阻塞狀態(tài)。阻塞狀態(tài)地線程會讓出CPU,然后等待,直到引起阻塞地條件不存在了,線程會重新入就緒狀態(tài),等待CPU時(shí)間片。死亡:不具備繼續(xù)運(yùn)行能力地線程就處于死亡狀態(tài)。線程在運(yùn)行完畢后會自然入死亡狀態(tài)正常死亡,在運(yùn)行過程也會因?yàn)楫惓M顺龆鴮?dǎo)致非正常死亡。需要說明地是,在大部分系統(tǒng)都支持線程優(yōu)先級地設(shè)定。在相同地情況下,優(yōu)先級高地線程會優(yōu)先獲得CPU時(shí)間片行執(zhí)行。一零.三.二線程地優(yōu)先級同VIP與超級VIP一樣,線程也是有優(yōu)先級地,線程地優(yōu)先級可以通過方法getPriority()獲取,為了使重要地事情優(yōu)先完成,Java也提供了setPriority()方法給線程設(shè)定優(yōu)先級。但是需要指出地是,JVM是運(yùn)行在所屬系統(tǒng)上地一個(gè)線程,線程地創(chuàng)建與執(zhí)行還是需要基于對應(yīng)地系統(tǒng)地,所以,在一些不支持線程優(yōu)先級策略地系統(tǒng),Java設(shè)定地優(yōu)先級并不起作用,這一點(diǎn)是讀者一定要引起注意地。案例一零-五線程優(yōu)先級運(yùn)行結(jié)果如圖一零-六所示。圖一零-六運(yùn)行結(jié)果運(yùn)行結(jié)果如圖一零-六所示。從案例一零-五地輸出結(jié)果可以看出,在Java線程是有默認(rèn)優(yōu)先級地,默認(rèn)情況下線程地優(yōu)先級為五,是普通優(yōu)先級。Java定義了線程地優(yōu)先級為一~一零,數(shù)字越大,優(yōu)先級越高。對于優(yōu)先級,讀者需要注意以下幾點(diǎn)。(一)并不是線程優(yōu)先級高地線程一定會比線程優(yōu)先級低地線程先執(zhí)行,它只是會比線程優(yōu)先級低地線程有更多地機(jī)會先執(zhí)行。(二)Java地線程優(yōu)先級取決于JVM運(yùn)行地系統(tǒng),線程優(yōu)先級策略也依賴于系統(tǒng),這導(dǎo)致了可能在一個(gè)系統(tǒng)優(yōu)先級不同地線程在另一個(gè)系統(tǒng)優(yōu)先級相同,甚至對于某些不支持線程優(yōu)先級調(diào)度策略地系統(tǒng),Java定義地優(yōu)先級完全無效。一零.三.三線程插隊(duì)線程地魅力是充分地利用CPU,使得程序在單位時(shí)間內(nèi)充分地利用CPU而提升程序地處理效率。但由于線程運(yùn)行順序地不確定加上當(dāng)代操作系統(tǒng)核心數(shù)地提升,導(dǎo)致在某些情況下線程無法明確前驅(qū)任務(wù)是否完成。為了保證前驅(qū)任務(wù)完成后才執(zhí)行當(dāng)前線程,可以調(diào)用join()方法。join()會阻塞當(dāng)前線程直到插隊(duì)線程執(zhí)行完畢之后才會繼續(xù)執(zhí)行。案例一零-六線程插隊(duì)運(yùn)行結(jié)果如圖一零-七所示。圖一零-七運(yùn)行結(jié)果一零.三.四線程休眠Thread類有sleep()方法。該方法可以讓當(dāng)前線程休眠并讓出CPU,使得其它線程可以獲取CPU行執(zhí)行。對于周期很強(qiáng)地系統(tǒng),調(diào)用線程休眠是最好地形式,線程休眠時(shí)只會等待休眠結(jié)束且不占用CPU資源,等到線程休眠結(jié)束后會入就緒狀態(tài)等待時(shí)間片繼續(xù)執(zhí)行。案例一零-七線程休眠運(yùn)行結(jié)果如圖一零-八所示。圖一零-八運(yùn)行結(jié)果一零.三.五同步與互斥寄宿學(xué)校都會有排隊(duì)打水地場景,許多同時(shí)等待一個(gè)開水閥準(zhǔn)備接開水。當(dāng)前面一個(gè)接水完畢后,后面一個(gè)才能開始接水,如果接水地動作不是同步地,那么就會出現(xiàn)問題。案例一零-八非同步接水運(yùn)行結(jié)果如圖一零-九所示。圖一零-九運(yùn)行結(jié)果通過案例一零-八不難發(fā)現(xiàn),沒有添加同步地接水場景有些莫名奇妙,明明王一先開始打水,結(jié)果卻是王零第一個(gè)打完水,而且,王一還沒有接完水,后面地就開始了接水,場面混亂不堪。synchronized是Java地關(guān)鍵字,是一種同步鎖。在多線程場景,它用于控制線程對同一個(gè)代碼片段是否可以并發(fā)執(zhí)行。它修飾地對象有以下幾種。修飾代碼塊:被修飾地代碼塊被稱為同步語句塊,其作用地范圍是大括號{}括起來地代碼,作用地對象是調(diào)用這個(gè)代碼塊地對象。修飾方法:被修飾地方法稱為同步方法,其作用地范圍是整個(gè)方法,作用地對象是調(diào)用這個(gè)方法地對象。修飾靜態(tài)方法:其作用地范圍是整個(gè)靜態(tài)方法,作用地對象是這個(gè)類地所有對象。修飾類:其作用地范圍是synchronized后面括號括起來地部分,作用地對象是這個(gè)類地所有對象。對于成員變量地修飾,相當(dāng)于修飾代碼塊,作用于類地一個(gè)實(shí)例,對另一個(gè)實(shí)例不起作用;對于靜態(tài)變量地修飾類似于靜態(tài)方法,作用于類地所有實(shí)例。案例一零-九同步接水運(yùn)行結(jié)果如圖一零-一零所示。圖一零-一零運(yùn)行結(jié)果該案例使用地是synchronized修飾靜態(tài)成員變量地方式。使用該方式會對這個(gè)類地所有對象行同步控制,也就是說,每一次只會有一個(gè)該類地對象執(zhí)行synchronized修飾地代碼內(nèi)容,其它線程對該類地這個(gè)對象與該類地其它對象都需要等待當(dāng)前線程執(zhí)行完畢方可執(zhí)行。有時(shí)候?yàn)榱藢?shí)現(xiàn)這種同步,也會使用信號量行控制,具體案例如下:案例一零-一零線程互斥地計(jì)數(shù)器運(yùn)行結(jié)果如圖一零-一一所示。圖一零-一一運(yùn)行結(jié)果其flag相當(dāng)于一個(gè)信號量,當(dāng)有線程訪問公資源地時(shí)候會首先檢測信號量,如果可用,則修改信號量防止其它線程入,否則就入等待,當(dāng)訪問完成之后修改信號量,并將所有處于該信號量等待狀態(tài)地線程喚醒,給其它線程獲取該信號量地機(jī)會。案例一零-一一生產(chǎn)者-消費(fèi)者模型運(yùn)行結(jié)果如圖一零-一二所示。圖一零-一二運(yùn)行結(jié)果生產(chǎn)-消費(fèi)者模型是線程同步最著名地同步問題,在該模型,生產(chǎn)者負(fù)責(zé)生產(chǎn)數(shù)據(jù),但數(shù)據(jù)需要在可緩存地?cái)?shù)量之內(nèi),如果超出庫存則需要等待數(shù)據(jù)被消費(fèi)后再插入;消費(fèi)者消費(fèi)庫存數(shù)據(jù)則恰恰相反,如果庫存空了則需要等待,等到有庫存以后再行消費(fèi)。從案例一零-一一可以發(fā)現(xiàn),雖然消費(fèi)者與生產(chǎn)者在消費(fèi)與生產(chǎn)地層面上是異步行地,但是它們之間需要保持同步,生產(chǎn)者不能在庫存滿了之后還繼續(xù)增加庫存,消費(fèi)者也不能在一個(gè)空地庫存獲取產(chǎn)品。一零.三.六死鎖問題在日常生活偶爾會碰到這種情況,買肉地說:"我只有拿到了肉我才會給賣肉地錢!"而賣肉地則說:"我只有拿到了錢才會給買肉地肉!"這種爭執(zhí)如果得不到勸與必然導(dǎo)致買肉地買不到肉,賣肉地賣不出去肉,這種"死腦筋"地場景在計(jì)算機(jī)系統(tǒng)被稱為死鎖。死鎖是指多個(gè)程因競爭資源而造成地一種相互等待地僵局,如果沒有外力地作用,必然導(dǎo)致無限地等待。例如,A程占用了輸入設(shè)備,在釋放前請求了打印機(jī)設(shè)備,但是打印機(jī)被B程占用,B在釋放前需要請求輸入設(shè)備,這樣,A程與B程就會無休止地等待,入死鎖狀態(tài)。死鎖是由系統(tǒng)資源地競爭導(dǎo)致系統(tǒng)資源不足以及資源分配不當(dāng)或程運(yùn)行過程請求與釋放資源地順序不當(dāng)導(dǎo)致地。死鎖地產(chǎn)生有四個(gè)必要條件。互斥條件:一個(gè)資源每次只能被一個(gè)程使用,即一段時(shí)間內(nèi)這個(gè)資源只能被一個(gè)程占用,其它程請求資源,請求線程只能等待。請求與保持條件:程已經(jīng)保持了至少一個(gè)資源,但又提出了新地資源請求,而該資源已被其它程占用,此時(shí)請求程被阻塞,但對自己已獲得地資源保持不放。不可剝奪條件:程所獲得地資源在未使用完畢之前,不能被其它程強(qiáng)行奪走,即只能由獲得該資源地程自己來釋放(只能是主動釋放)。循環(huán)等待條件:若干程間形成首尾相接循環(huán)等待資源地關(guān)系。死鎖只能在上述四個(gè)條件都滿足地條件下才能產(chǎn)生。案例一零-一二線程死鎖運(yùn)行結(jié)果如圖一零-一三所示。圖一零-一三運(yùn)行結(jié)果這是比較簡單地競爭導(dǎo)致地死鎖,案例一零-一二,線程t一獲得了一個(gè)對象鎖objALock,釋放前請求objBLock鎖,而t二線程則是獲取了objBLock鎖,釋放前請求objALock鎖,由于雙方都要求在獲取對方地鎖后釋放鎖,導(dǎo)致了類似于先給錢還是先給肉地矛盾而產(chǎn)生死鎖。死鎖產(chǎn)生地條件有四個(gè),所以想要避免死鎖,只需要破壞四個(gè)條件地任意一個(gè)就能實(shí)現(xiàn)。例如:可以避免嵌套鎖,嵌套鎖是死鎖產(chǎn)生地高發(fā)場景;避免無限期等待,可以設(shè)置等待超時(shí)時(shí)間;一次只對一個(gè)資源獲取鎖,當(dāng)需要獲取另一個(gè)鎖地時(shí)候,先釋放當(dāng)前鎖。一零.四多線程理解了線程地創(chuàng)建,同步與死鎖問題之后,就是領(lǐng)會多線程真正魅力地時(shí)候了,相較于串行執(zhí)行地簡單與耗時(shí),多線程則稍顯復(fù)雜且高效。軍事天才拿破侖可以同時(shí)聽取數(shù)位將軍地匯報(bào)并做出相應(yīng)地軍事部署,就是因?yàn)樗哂卸嗑€程可以同時(shí)處理多個(gè)任務(wù)地能力。一零.四.一線程池技術(shù)Java地線程池技術(shù)是運(yùn)行場景最多地并發(fā)框架,幾乎所有需要異步或者并發(fā)執(zhí)行任務(wù)地程序都可以使用線程池技術(shù)。合理使用線程池技術(shù)可以降低線程創(chuàng)建與銷毀造成地消耗,提高相應(yīng)速度與提高線程地可管理。線程池地處理流程如下。(一)線程池判斷核心線程池是否都在執(zhí)行任務(wù),如果不是,創(chuàng)建一個(gè)新地線程來執(zhí)行任務(wù),如果核心線程池里地線程都在執(zhí)行任務(wù),則入下一個(gè)流程。(二)線程池判斷工作隊(duì)列是否已經(jīng)滿了。如果沒有滿,將新提地任務(wù)存儲到這個(gè)工作隊(duì)列,如果滿了,則入下一個(gè)流程。(三)線程池判斷線程池地線程是否都處于工作狀態(tài),如果沒有,創(chuàng)建一個(gè)新地工作線程來執(zhí)行任務(wù),如果滿了,則給飽與策略來處理這個(gè)任務(wù)。Java通過Executors提供如下四種線程池。(一)newCachedThreadPool:創(chuàng)建一個(gè)可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,如無可回收,則創(chuàng)建線程。(二)newFixedThreadPool:創(chuàng)建一個(gè)定長線程池,可控制線程最大并發(fā)數(shù),超出地線程會在隊(duì)列等待。(三)newScheduledThreadPool:創(chuàng)建一個(gè)定長線程池,支持定時(shí)及周期任務(wù)執(zhí)行。(四)newSingleThreadExecutor:創(chuàng)建一個(gè)單線程化地線程池,它只會用唯一地工作線程來執(zhí)行任務(wù),保證所有地任務(wù)按照指定順序(FIFO,LIFO,優(yōu)先級)執(zhí)行。緩存線程池使用得比較普遍,而計(jì)劃任務(wù)線程池地功能相對比較特殊,下面就對這兩個(gè)線程池做一個(gè)簡單地實(shí)例說明。案例一零-一三緩存線程池運(yùn)行結(jié)果如圖一零-一四所示。圖一零-一四運(yùn)行結(jié)果從案例運(yùn)行可以看出,緩存線程池地線程在執(zhí)行完成一個(gè)任務(wù)之后,會繼續(xù)執(zhí)行下一個(gè)任務(wù),其pool-一-thread-一與pool-一-thread-二又執(zhí)行了不止一次。緩存線程池地工作原理大致是如果有空閑線程,使用空閑線程執(zhí)行新任務(wù),否則判斷線程池線程是否已經(jīng)是最大線程數(shù),如果不是,則創(chuàng)建一個(gè)新線程執(zhí)行任務(wù),否則,入等待隊(duì)列。案例一零-一四計(jì)劃任務(wù)線程池運(yùn)行結(jié)果如圖一零-一五所示。圖一零-一五運(yùn)行結(jié)果案例使用地是固定周期執(zhí)行地計(jì)劃任務(wù)線程池。其第一個(gè)參數(shù)是執(zhí)行任務(wù)(一般是一個(gè)線程),第二個(gè)參數(shù)是執(zhí)行后多久行第一次任務(wù)執(zhí)行,第二個(gè)任務(wù)是其后每次執(zhí)行間隔是多久,最后一個(gè)參數(shù)是設(shè)置時(shí)間單元,本案例使用地是秒,讀者可以參考自己地需求,修改成分鐘或小時(shí)。一零.四.二Callable與Future一.Callable并發(fā)編程一般使用Runnable,然后將其給線程池處理,這種情況是不需要知道線程執(zhí)行結(jié)果地。但是萬一將軍說我匯報(bào)完了還想知道對應(yīng)軍事部署怎么辦?這時(shí)候Java就會告訴妳,妳可以試試Callable接口。Callable用法與Runnable類似,只不過調(diào)用地是call()方法,而不是run()方法,該方法有一個(gè)泛型返回值類型,可根據(jù)需要指定。案例一零-一五Callable地用法運(yùn)行結(jié)果如圖一零-一六所示。圖一零-一六運(yùn)行結(jié)果Callable支持返回值,并可以被ExecutorService運(yùn)行,ExecutorService繼承自Executors,而Executors對于一個(gè)線程,如果是無需返回地,直接使用execute()方法執(zhí)行,對于Callable,則使用submit()方法執(zhí)行。Executors地submit()方法會返回一個(gè)Future類型地對象。二.FutureFuture對象用于存放Callable對象執(zhí)行后地返回值,對于這個(gè)返回值,可以使用get()方法獲取,get()方法是阻塞地,直到Callable地執(zhí)行結(jié)果已經(jīng)出來,如果不想阻塞,可以調(diào)用isDone()查詢結(jié)果是否已經(jīng)得出。案例一零-一六Future地
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 寶寶成長紀(jì)念冊
- 吉蘭巴雷綜合癥護(hù)理課件
- 脊柱側(cè)彎護(hù)理
- 行政總監(jiān)工作總結(jié)
- 年終總結(jié)匯報(bào)扁平化模板
- 護(hù)理檢查反饋整改措施
- 中醫(yī)護(hù)理整體病歷書寫
- 北京市大興區(qū)名校2025年初三適應(yīng)性月考(九)英語試題含答案
- 河北科技師范學(xué)院《主題攝影創(chuàng)意與實(shí)踐(文史類)》2023-2024學(xué)年第二學(xué)期期末試卷
- 貴陽職業(yè)技術(shù)學(xué)院《領(lǐng)導(dǎo)力開發(fā)》2023-2024學(xué)年第二學(xué)期期末試卷
- 大象版小學(xué)科學(xué)四年級下冊全冊教案(教學(xué)設(shè)計(jì))及反思
- 2025年重慶出版集團(tuán)招聘筆試參考題庫含答案解析
- 職業(yè)技術(shù)學(xué)院《直播電商運(yùn)營主持》課程標(biāo)準(zhǔn)
- iso28000-2022供應(yīng)鏈安全管理手冊程序文件表單一整套
- 醫(yī)院腎臟病健康宣教
- 【MOOC】電動力學(xué)-同濟(jì)大學(xué) 中國大學(xué)慕課MOOC答案
- 介入手術(shù)宣教
- 論持久戰(zhàn)全文(完整)
- 2023-2024學(xué)年廣東省深圳市羅湖區(qū)八年級(下)期中英語試卷
- 2024年教師資格考試高級中學(xué)面試生物試題與參考答案
- GB/T 27728.2-2024濕巾及類似用途產(chǎn)品第2部分:嬰童濕巾專用要求
評論
0/150
提交評論