




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
1、新一代信息技術(shù)“十三五”系列規(guī)劃教材 Java 程序設(shè)計(jì)基礎(chǔ)教程(慕課版)劉剛 劉偉 編著第10章 并發(fā)編程 支持多線程是現(xiàn)代操作系統(tǒng)的一大特點(diǎn),多線程的操作系統(tǒng)因?yàn)榭梢哉嬲饬x上地實(shí)現(xiàn)多任務(wù)同時運(yùn)行,極大地提升了操作系統(tǒng)的處理速度??缙脚_的特性導(dǎo)致Java無法像C/C+這些語言一樣通過調(diào)用系統(tǒng)API來實(shí)現(xiàn)多線程程序,所以它在語言本身加了對多線程的支持。這些功能都以面向?qū)ο蟮姆绞絹韺?shí)現(xiàn),更加易于理解和使用。10.1 線程與進(jìn)程 在操作系統(tǒng)中,通常將進(jìn)程看作是系統(tǒng)資源分配和運(yùn)行的基本單位,一個任務(wù)就是一個進(jìn)程。進(jìn)程擁有獨(dú)立的系統(tǒng)資源,包含CPU、內(nèi)存和輸入輸出端口等,例如打開的瀏覽器和Word文
2、檔,這些相對獨(dú)立的資源表明了進(jìn)程具有動態(tài)性、并發(fā)性、獨(dú)立性和異步性等特點(diǎn)。 線程(thread)是“進(jìn)程”中某個單一順序的控制流,被稱為輕量級進(jìn)程(lightweight processes),是比進(jìn)程更小的執(zhí)行單位,也是程序執(zhí)行流中最小的單位。一個標(biāo)準(zhǔn)的線程由線程ID、當(dāng)前指令指針(PC)、寄存器集合和堆棧組成。線程是進(jìn)程中的一個實(shí)體,是被系統(tǒng)獨(dú)立調(diào)度和分配的基本單位,線程在運(yùn)行中的資源歸屬于進(jìn)程,同屬于一個進(jìn)程的所有線程共享該進(jìn)程所擁有的系統(tǒng)資源。 一個線程可以創(chuàng)建和撤銷另一個線程,同一個進(jìn)程中的多個線程也可以并發(fā)執(zhí)行。由于進(jìn)程所有資源是固定的且線程間存在相互制約,使得線程可能處于就緒、阻
3、塞和運(yùn)行等狀態(tài),令線程的執(zhí)行呈現(xiàn)出間斷性。線程之間可以共享代碼和數(shù)據(jù)、實(shí)時通信、進(jìn)行必要的同步操作等。一個程序都至少擁有一個進(jìn)程;每個進(jìn)程擁有一個或者多個線程。每個線程都有自己獨(dú)立的資源和生命周期。 進(jìn)程和線程的最大區(qū)別在于,進(jìn)程是由操作系統(tǒng)來控制的,而線程則是由進(jìn)程來控制的。進(jìn)程都是相互獨(dú)立的,各自享有各自的內(nèi)存空間,因此進(jìn)程間的通信是昂貴且受限的,進(jìn)程間的轉(zhuǎn)換也是需要開銷的;線程則共享進(jìn)程的內(nèi)存空間,線程通信是便宜的且線程間的轉(zhuǎn)換也是低成本的,這種低成本低開銷的通信也可能會產(chǎn)生意想不到的錯誤:當(dāng)多個線程訪問同一個變量時,獲取到的值是不一樣的!不過,也不必?fù)?dān)心,這些問題可以通過同步機(jī)制和鎖機(jī)
4、制來消除。10.2 線程的創(chuàng)建 多線程技術(shù)是Java語言的重要特性之一,Java平臺提供了一套廣泛且功能強(qiáng)大的API、工具和技術(shù)。Java編寫的程序都運(yùn)行在Java虛擬機(jī)(JVM)中。在JVM內(nèi)部,程序的多任務(wù)是通過線程來實(shí)現(xiàn)的。在同一個JVM進(jìn)程中,有且只有一個進(jìn)程,那就是JVM本身,在JVM環(huán)境中,所有的程序代碼都是以線程來運(yùn)行的。 Java中的線程有兩種實(shí)現(xiàn)方式,一種是繼承Thread類,一種是實(shí)現(xiàn)Runnable接口。但是無論是哪種方式,線程都要使用到Thread類及其相關(guān)方法。10.2.1 繼承Thread類 Thread類是一個實(shí)體類,該類封裝了線程的行為,想要利用Thread創(chuàng)建
5、一個線程,必須創(chuàng)建一個從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)造方法用途各異,如表10-1所示。構(gòu)造方法說明Thread()構(gòu)造一個線程對象Thread(Runnable target)構(gòu)造一個線程對象,target是被創(chuàng)建線程的目標(biāo)對象,它實(shí)現(xiàn)了Runnable接口中的run()方法Thread(String name)以指定名稱構(gòu)造一個線程對象Thread(ThreadGroup group, Runnable target)
6、在指定線程組中構(gòu)造一個線程對象,使用目標(biāo)對象的target的run()方法Thread(Runnable target, String name)以指定名稱構(gòu)造一個線程對象,使用目標(biāo)對象target的run()方法Thread(ThreadGroup group, Runnable target, String name)在指定的線程組中創(chuàng)建一個指定名稱的線程,使用目標(biāo)對象target的run()方法Thread(ThreadGroup group, Runnable target, String name, long stackSize)在指定線程組中構(gòu)造一個線程對象,以name作為線程的名
7、字,使用目標(biāo)對象target的run()方法,stackSize指定堆棧大小表10-1 Thread類的構(gòu)造方法 Thread也提供了很多輔助方法,以讓線程正常運(yùn)行和方便程序員對線程的控制,其常用方法如表10-2所示。方法名說明static int activeCount()返回線程組中正在運(yùn)行的線程的數(shù)目void checkAccess()確定當(dāng)前運(yùn)行的線程是否有權(quán)限修改線程static Thread currentThread()返回當(dāng)前正在執(zhí)行的線程void destroy()銷毀線程,但不回收資源static void dumpStack()顯示當(dāng)前線程的堆棧信息long getId(
8、)返回當(dāng)前線程的id值String getName()返回當(dāng)前線程的名稱int getPriority()返回當(dāng)前線程的優(yōu)先級Thread.State getState()返回當(dāng)前線程的狀態(tài)ThreadGroup getThreadGroup()返回當(dāng)前線程所屬的線程組void interrupt()中斷線程boolean isAlive()判斷當(dāng)前線程是否存活boolean isDaemon()判斷當(dāng)前線程是否是守護(hù)線程boolean isInterrupted()判斷本線程是否被中斷void join()等待直到線程死亡void join(long millis)等待最多millis毫秒,
9、直到線程死亡void run()如果類是使用單獨(dú)的Runnable對象構(gòu)造的,將調(diào)用Runnable對象的run()方法,否則本方法不做任何事情就返回了,如果是子類繼承Thread類,請務(wù)必實(shí)現(xiàn)本方法以覆蓋父類void setDaemon(boolean on)將當(dāng)前線程設(shè)置為守護(hù)線程void setName(String name)將當(dāng)前線程名稱修改為namevoid setPriority(int newPriority)設(shè)置當(dāng)前線程的優(yōu)先級static void sleep(long millis)線程休眠millis毫秒void start()啟動線程,JVM會自動調(diào)用run()方法s
10、tatic void yield()暫停當(dāng)前線程,同時允許其他線程運(yùn)行表10-2 常用的Thread方法 在以前的案例中,當(dāng)需要執(zhí)行當(dāng)前類時,每個類都有一個main()方法。該方法是類的入口,JVM會找到該入口方法并運(yùn)行,此時產(chǎn)生了一個線程,該線程便是主線程。當(dāng)main()方法運(yùn)行結(jié)束后,主線程運(yùn)行完成,JVM也就隨即退出了。JVM負(fù)責(zé)對進(jìn)程、線程進(jìn)行管理,JVM分配時間片(CPU時間)給線程,線程按照系統(tǒng)的設(shè)定輪流獲取時間片執(zhí)行,切換時間很短,在對線程運(yùn)行效率要求不嚴(yán)格的場景下可以忽略不計(jì)。案例10-1 Thread實(shí)現(xiàn)多線程 運(yùn)行結(jié)果如圖10-1所示。圖10-1 運(yùn)行結(jié)果 由于每個線程運(yùn)行
11、的次數(shù)較少,所以線程默認(rèn)優(yōu)先級下的運(yùn)行隨機(jī)性不是很明顯,但通過方框標(biāo)注的線程Thread-3的運(yùn)行可以看出,實(shí)際上線程運(yùn)行并不是順序的。案例10-2 Thread的部分方法使用 運(yùn)行結(jié)果如圖10-2所示。圖10-2 運(yùn)行結(jié)果案例10-3 start方法和run方法 運(yùn)行結(jié)果如圖10-3所示。圖10-3 運(yùn)行結(jié)果 啟動Thread類時,必須要使用start()方法啟動一個線程,如果直接調(diào)用run()方法,則JVM認(rèn)為這只是一次普通的方法調(diào)用,而非需要啟動一個線程在執(zhí)行run()方法內(nèi)部的邏輯。讀者在使用線程的時候切記。在start()方法調(diào)用后也可以看出,運(yùn)行的是兩個線程的代碼,而且它們之間互不
12、干擾地同時執(zhí)行。所以一些工作交給線程去做的時候,啟動一個新線程的線程可以做自己想做的其他事情,而無需等到新線程的執(zhí)行結(jié)束。10.2.2 實(shí)現(xiàn)Runnable接口 實(shí)現(xiàn)多線程的另一個方式是實(shí)現(xiàn)Runnable接口。 Runnable只有一個方法,即run()方法,該方法需要由一個實(shí)現(xiàn)了此接口的類來實(shí)現(xiàn)。實(shí)現(xiàn)了Runnable接口的類的對象需要由Thread類的一個實(shí)例內(nèi)部運(yùn)行它,其本身不能直接運(yùn)行。案例10-4 Runnable實(shí)現(xiàn)多線程 運(yùn)行結(jié)果如圖10-4所示。 圖10-4 運(yùn)行結(jié)果 圖10-4只摘取部分的輸出內(nèi)容,從內(nèi)容上看,實(shí)現(xiàn)Runnable和繼承Thread都能達(dá)到相同目的,都能啟動
13、一個新線程。唯一的區(qū)別是Runnable對象必須包裝成Thread對象后才能運(yùn)行。如果查看Thread和Runnable類源碼會發(fā)現(xiàn),Thread類實(shí)際上是Runnable的一個實(shí)現(xiàn)類??赡苡凶x者會對Runnable接口的存在產(chǎn)生疑問,畢竟這個接口只有一個run()方法。Runnable的存在是因?yàn)镴ava的類有且只能有一個直接父類,如果只是提供了Thread類,那么想要繼承其他類且需要同時繼承Thread類的這個子類,在實(shí)現(xiàn)這種繼承邏輯上會產(chǎn)生很多困難,而Runnable則避免了這種尷尬局面的出現(xiàn),在Java中,一個類是可以實(shí)現(xiàn)多個接口的。10.3 線程的調(diào)度 在JVM中,線程只有在獲取了C
14、PU分配的時間片后才會真正地執(zhí)行,在線程創(chuàng)建后到死亡的這個過程中還有其他的線程狀態(tài),這些狀態(tài)組成了線程的生命周期。 10.3.1 線程的生命周期 如同生命體一般,線程也有生命周期,線程的生命周期是從線程新建開始,一直持續(xù)到線程死亡。在新建和死亡之間,線程還有就緒、阻塞和運(yùn)行狀態(tài),一個線程會在這5種狀態(tài)間轉(zhuǎn)換,最終完成自己的使命。 線程的狀態(tài)及轉(zhuǎn)換關(guān)系如圖10-5所示。圖10-5 Java線程狀態(tài)轉(zhuǎn)換圖 線程各個狀態(tài)的說明如下。 新建:當(dāng)創(chuàng)建一個Thread類和它的子類、對象后,線程就處于新建狀態(tài),這種狀態(tài)的線程并不具備運(yùn)行的能力,該操作對于系統(tǒng)而言,僅僅消耗普通對象創(chuàng)建時會消耗的非CPU資源。
15、 就緒:當(dāng)處于新建狀態(tài)的線程調(diào)用start()方法被啟動之后,線程將進(jìn)入線程隊(duì)列等待CPU時間片,進(jìn)行執(zhí)行。此時的線程才具備了運(yùn)行的能力,一旦獲取了時間片線程就執(zhí)行。 運(yùn)行:就緒狀態(tài)的線程獲取了時間片之后,就進(jìn)入了運(yùn)行狀態(tài),此時線程會執(zhí)行run()方法內(nèi)的代碼邏輯。線程一旦進(jìn)入運(yùn)行狀態(tài),就與啟動該線程的線程沒有任何關(guān)系了,兩者平行運(yùn)行,互不影響。 阻塞:線程在運(yùn)行的過程中因資源無法滿足、前驅(qū)任務(wù)沒有完成或者被調(diào)用阻塞方法都會導(dǎo)致線程進(jìn)入阻塞狀態(tài)。阻塞狀態(tài)的線程會讓出CPU,然后等待,直到引起阻塞的條件不存在了,線程會重新進(jìn)入就緒狀態(tài),等待CPU時間片。 死亡:不具備繼續(xù)運(yùn)行能力的線程就處于死亡
16、狀態(tài)。線程在運(yùn)行完畢后會自然進(jìn)入死亡狀態(tài)正常死亡,在運(yùn)行過程中也會因?yàn)楫惓M顺龆鴮?dǎo)致非正常死亡。 需要說明的是,在大部分系統(tǒng)中都支持線程優(yōu)先級的設(shè)定。在相同的情況下,優(yōu)先級高的線程會優(yōu)先獲得CPU時間片進(jìn)行執(zhí)行。10.3.2 線程的優(yōu)先級 同VIP和超級VIP一樣,線程也是有優(yōu)先級的,線程的優(yōu)先級可以通過方法getPriority()獲取,為了使重要的事情優(yōu)先完成,Java也提供了setPriority()方法給線程設(shè)定優(yōu)先級。但是需要指出的是,JVM是運(yùn)行在所屬系統(tǒng)上的一個線程,線程的創(chuàng)建和執(zhí)行還是需要基于對應(yīng)的系統(tǒng)的,所以,在一些不支持線程優(yōu)先級策略的系統(tǒng)中,Java設(shè)定的優(yōu)先級并不起作用
17、,這一點(diǎn)是讀者一定要引起注意的。案例10-5 線程優(yōu)先級 運(yùn)行結(jié)果如圖10-6所示。 圖10-6 運(yùn)行結(jié)果 運(yùn)行結(jié)果如圖10-6所示。 從案例10-5的輸出結(jié)果可以看出,在Java中線程是有默認(rèn)優(yōu)先級的,默認(rèn)情況下線程的優(yōu)先級為5,是普通優(yōu)先級。Java中定義了線程的優(yōu)先級為110,數(shù)字越大,優(yōu)先級越高。 對于優(yōu)先級,讀者需要注意以下幾點(diǎn)。(1)并不是線程優(yōu)先級高的線程一定會比線程優(yōu)先級低的線程先執(zhí)行,它只是會比線程優(yōu)先級低的線程有更多的機(jī)會先執(zhí)行。(2)Java的線程優(yōu)先級取決于JVM運(yùn)行的系統(tǒng),線程優(yōu)先級策略也依賴于系統(tǒng),這導(dǎo)致了可能在一個系統(tǒng)中優(yōu)先級不同的線程在另一個系統(tǒng)中優(yōu)先級相同,甚
18、至對于某些不支持線程優(yōu)先級調(diào)度策略的系統(tǒng),Java定義的優(yōu)先級完全無效。10.3.3 線程插隊(duì) 線程的魅力是充分地利用CPU,使得程序在單位時間內(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í)行。案例10-6 線程插隊(duì) 運(yùn)行結(jié)果如圖10-7所示。圖10-7 運(yùn)行結(jié)果10.3.4 線程休眠 Thread類中有sleep()方法。該方法可以讓當(dāng)前線程休眠并讓出CPU,使得其他線程可
19、以獲取CPU進(jìn)行執(zhí)行。對于周期性很強(qiáng)的系統(tǒng),調(diào)用線程休眠是最好的形式,線程休眠時只會等待休眠結(jié)束且不占用CPU資源,等到線程休眠結(jié)束后會進(jìn)入就緒狀態(tài)等待時間片繼續(xù)執(zhí)行。案例10-7 線程休眠 運(yùn)行結(jié)果如圖10-8所示。圖10-8 運(yùn)行結(jié)果10.3.5 同步與互斥 寄宿學(xué)校都會有排隊(duì)打水的場景,許多人同時等待一個開水閥準(zhǔn)備接開水。當(dāng)前面一個人接水完畢后,后面一個人才能開始接水,如果接水的動作不是同步的,那么就會出現(xiàn)問題。案例10-8 非同步接水 運(yùn)行結(jié)果如圖10-9所示。圖10-9 運(yùn)行結(jié)果 通過案例10-8不難發(fā)現(xiàn),沒有添加同步的接水場景有些莫名奇妙,明明王1先開始打水,結(jié)果卻是王0第一個打完
20、水,而且,王1還沒有接完水,后面的人就開始了接水,場面混亂不堪。 synchronized是Java中的關(guān)鍵字,是一種同步鎖。在多線程場景中,它用于控制線程對同一個代碼片段是否可以并發(fā)執(zhí)行。它修飾的對象有以下幾種。 修飾代碼塊:被修飾的代碼塊被稱為同步語句塊,其作用的范圍是大括號括起來的代碼,作用的對象是調(diào)用這個代碼塊的對象。 修飾方法:被修飾的方法稱為同步方法,其作用的范圍是整個方法,作用的對象是調(diào)用這個方法的對象。 修飾靜態(tài)方法:其作用的范圍是整個靜態(tài)方法,作用的對象是這個類的所有對象。 修飾類:其作用的范圍是synchronized后面括號括起來的部分,作用的對象是這個類的所有對象。 對
21、于成員變量的修飾,相當(dāng)于修飾代碼塊,作用于類的一個實(shí)例,對另一個實(shí)例不起作用;對于靜態(tài)變量的修飾類似于靜態(tài)方法,作用于類的所有實(shí)例。案例10-9 同步接水 運(yùn)行結(jié)果如圖10-10所示。圖10-10 運(yùn)行結(jié)果 該案例使用的是synchronized修飾靜態(tài)成員變量的方式。使用該方式會對這個類的所有對象進(jìn)行同步控制,也就是說,每一次只會有一個該類的對象執(zhí)行synchronized修飾的代碼內(nèi)容,其他線程對該類的這個對象和該類的其他對象都必須等待當(dāng)前線程執(zhí)行完畢方可執(zhí)行。 有時候?yàn)榱藢?shí)現(xiàn)這種同步,也會使用信號量進(jìn)行控制,具體案例如下:案例10-10 線程互斥的計(jì)數(shù)器 運(yùn)行結(jié)果如圖10-11所示。圖1
22、0-11 運(yùn)行結(jié)果 其中flag相當(dāng)于一個信號量,當(dāng)有線程訪問公共資源的時候會首先檢測信號量,如果可用,則修改信號量防止其他線程進(jìn)入,否則就進(jìn)入等待,當(dāng)訪問完成之后修改信號量,并將所有處于該信號量等待狀態(tài)的線程喚醒,給其他線程獲取該信號量的機(jī)會。案例10-11 生產(chǎn)者-消費(fèi)者模型 運(yùn)行結(jié)果如圖10-12所示。 圖10-12 運(yùn)行結(jié)果 生產(chǎn)-消費(fèi)者模型是線程同步中最著名的同步問題,在該模型中,生產(chǎn)者負(fù)責(zé)生產(chǎn)數(shù)據(jù),但數(shù)據(jù)需要在可緩存的數(shù)量之內(nèi),如果超出庫存則需要等待數(shù)據(jù)被消費(fèi)后再插入;消費(fèi)者消費(fèi)庫存數(shù)據(jù)則恰恰相反,如果庫存空了則需要等待,等到有庫存以后再進(jìn)行消費(fèi)。從案例10-11中可以發(fā)現(xiàn),雖然消
23、費(fèi)者和生產(chǎn)者在消費(fèi)和生產(chǎn)的層面上是異步進(jìn)行的,但是他們之間必須保持同步,生產(chǎn)者不能在庫存滿了之后還繼續(xù)增加庫存,消費(fèi)者也不能在一個空的庫存中獲取產(chǎn)品。10.3.6 死鎖問題 在日常生活中偶爾會碰到這種情況,買肉的說:“我只有拿到了肉我才會給賣肉的錢!”而賣肉的則說:“我只有拿到了錢才會給買肉的肉!”這種爭執(zhí)如果得不到勸和必然導(dǎo)致買肉的買不到肉,賣肉的賣不出去肉,這種“死腦筋”的場景在計(jì)算機(jī)系統(tǒng)中被稱為死鎖。 死鎖是指多個進(jìn)程因競爭資源而造成的一種相互等待的僵局,如果沒有外力的作用,必然導(dǎo)致無限的等待。例如,A進(jìn)程占用了輸入設(shè)備,在釋放前請求了打印機(jī)設(shè)備,但是打印機(jī)被B進(jìn)程占用,B在釋放前需要請
24、求輸入設(shè)備,這樣,A進(jìn)程和B進(jìn)程就會無休止地等待,進(jìn)入死鎖狀態(tài)。 死鎖是由系統(tǒng)資源的競爭導(dǎo)致系統(tǒng)資源不足以及資源分配不當(dāng)或進(jìn)程運(yùn)行過程中請求和釋放資源的順序不當(dāng)導(dǎo)致的。死鎖的產(chǎn)生有4個必要條件。 互斥條件:一個資源每次只能被一個進(jìn)程使用,即一段時間內(nèi)這個資源只能被一個進(jìn)程占用,其他進(jìn)程請求資源,請求線程只能等待。 請求與保持條件:進(jìn)程已經(jīng)保持了至少一個資源,但又提出了新的資源請求,而該資源已被其他進(jìn)程占用,此時請求進(jìn)程被阻塞,但對自己已獲得的資源保持不放。 不可剝奪條件:進(jìn)程所獲得的資源在未使用完畢之前,不能被其他進(jìn)程強(qiáng)行奪走,即只能由獲得該資源的進(jìn)程自己來釋放(只能是主動釋放)。 循環(huán)等待條
25、件:若干進(jìn)程間形成首尾相接循環(huán)等待資源的關(guān)系。 死鎖只能在上述4個條件都滿足的條件下才能產(chǎn)生。案例10-12 線程死鎖 運(yùn)行結(jié)果如圖10-13所示。圖10-13 運(yùn)行結(jié)果 這是比較簡單的競爭導(dǎo)致的死鎖,案例10-12中,線程t1獲得了一個對象鎖objALock,釋放前請求objBLock鎖,而t2線程則是獲取了objBLock鎖,釋放前請求objALock鎖,由于雙方都要求在獲取對方的鎖后釋放鎖,導(dǎo)致了類似于先給錢還是先給肉的矛盾而產(chǎn)生死鎖。 死鎖產(chǎn)生的條件有四個,所以想要避免死鎖,只需要破壞四個條件中的任意一個就能實(shí)現(xiàn)。例如:可以避免嵌套鎖,嵌套鎖是死鎖產(chǎn)生的高發(fā)場景;避免無限期等待,可以設(shè)
26、置等待超時時間;一次只對一個資源獲取鎖,當(dāng)需要獲取另一個鎖的時候,先釋放當(dāng)前鎖。10.4 多線程 理解了線程的創(chuàng)建、同步和死鎖問題之后,就是領(lǐng)會多線程真正魅力的時候了,相較于串行執(zhí)行的簡單和耗時,多線程則稍顯復(fù)雜且高效。軍事天才拿破侖可以同時聽取數(shù)位將軍的匯報并做出相應(yīng)的軍事部署,就是因?yàn)樗哂卸嗑€程可以同時處理多個任務(wù)的能力。10.4.1 線程池技術(shù) Java中的線程池技術(shù)是運(yùn)行場景最多的并發(fā)框架,幾乎所有需要異步或者并發(fā)執(zhí)行任務(wù)的程序都可以使用線程池技術(shù)。合理使用線程池技術(shù)可以降低線程創(chuàng)建和銷毀造成的消耗,提高相應(yīng)速度和提高線程的可管理性。 線程池的處理流程如下。(1)線程池判斷核心線程池
27、是否都在執(zhí)行任務(wù),如果不是,創(chuàng)建一個新的線程來執(zhí)行任務(wù),如果核心線程池里的線程都在執(zhí)行任務(wù),則進(jìn)入下一個流程。(2)線程池判斷工作隊(duì)列是否已經(jīng)滿了。如果沒有滿,將新提交的任務(wù)存儲到這個工作隊(duì)列中,如果滿了,則進(jìn)入下一個流程。(3)線程池判斷線程池的線程是否都處于工作狀態(tài),如果沒有,創(chuàng)建一個新的工作線程來執(zhí)行任務(wù),如果滿了,則交給飽和策略來處理這個任務(wù)。 Java通過Executors提供如下4種線程池。(1)newCachedThreadPool:創(chuàng)建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,如無可回收,則創(chuàng)建線程。(2)newFixedThreadPool:創(chuàng)建一個定
28、長線程池,可控制線程最大并發(fā)數(shù),超出的線程會在隊(duì)列中等待。(3)newScheduledThreadPool:創(chuàng)建一個定長線程池,支持定時及周期性任務(wù)執(zhí)行。(4)newSingleThreadExecutor:創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任務(wù),保證所有的任務(wù)按照指定順序(FIFO、LIFO、優(yōu)先級)執(zhí)行。 緩存線程池使用得比較普遍,而計(jì)劃任務(wù)線程池的功能相對比較特殊,下面就對這兩個線程池做一個簡單的實(shí)例說明。案例10-13 緩存線程池 運(yùn)行結(jié)果如圖10-14所示。圖10-14 運(yùn)行結(jié)果 從案例運(yùn)行可以看出,緩存線程池的線程在執(zhí)行完成一個任務(wù)之后,會繼續(xù)執(zhí)行下一個任務(wù),
29、其中pool-1-thread-1和pool-1-thread-2又執(zhí)行了不止一次。緩存線程池的工作原理大致是如果有空閑線程,使用空閑線程執(zhí)行新任務(wù),否則判斷線程池線程是否已經(jīng)是最大線程數(shù),如果不是,則創(chuàng)建一個新線程執(zhí)行任務(wù),否則,進(jìn)入等待隊(duì)列。案例10-14 計(jì)劃任務(wù)線程池 運(yùn)行結(jié)果如圖10-15所示。 圖10-15 運(yùn)行結(jié)果 案例使用的是固定周期執(zhí)行的計(jì)劃任務(wù)線程池。其中第1個參數(shù)是執(zhí)行任務(wù)(一般是一個線程),第2個參數(shù)是執(zhí)行后多久進(jìn)行第一次任務(wù)執(zhí)行,第2個任務(wù)是其后每次執(zhí)行間隔是多久,最后一個參數(shù)是設(shè)置時間單元,本案例中使用的是秒,讀者可以參考自己的需求,修改成分鐘或小時。10.4.2
30、Callable和Future1Callable 并發(fā)編程一般使用Runnable,然后將其交給線程池處理,這種情況是不需要知道線程執(zhí)行結(jié)果的。但是萬一將軍說我匯報完了還想知道對應(yīng)軍事部署怎么辦?這時候Java就會告訴你,你可以試試Callable接口。 Callable用法和Runnable類似,只不過調(diào)用的是call()方法,而不是run()方法,該方法有一個泛型返回值類型,可根據(jù)需要指定。案例10-15 Callable的用法 運(yùn)行結(jié)果如圖10-16所示。 圖10-16 運(yùn)行結(jié)果 Callable支持返回值,并可以被ExecutorService運(yùn)行,ExecutorService繼承自Executors,而Executors對于一個線程,如果是無需返回的,直接使用execute()方法執(zhí)行,對于Callable,則使用submit()方法執(zhí)行。Executors的submit()方法會返回一個Future類型的對象。2Future Future對象用于存放Callable對象執(zhí)行后的返回值,對于這個返回值,可以使用get()方法獲取,get()方法是阻塞的,直到Callable的執(zhí)行結(jié)果已經(jīng)出來,如果不想阻塞,可以調(diào)用isDone()查詢結(jié)果是否已經(jīng)得出。案例10-16 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)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 市總工會領(lǐng)導(dǎo)班子述職報告
- 斯瑪特維商場互動導(dǎo)視系統(tǒng)解決方案v10(16-9)
- 2025年會計(jì)、審計(jì)及稅務(wù)服務(wù)合作協(xié)議書
- 廣東省廣州市華僑、協(xié)和、增城中學(xué)等三校2024~2025學(xué)年高一下學(xué)期期中考試數(shù)學(xué)試卷(解析版)
- 安徽省鼎尖聯(lián)考2024-2025學(xué)年高二下學(xué)期4月月考數(shù)學(xué)試題(解析)
- 2025年駕校學(xué)車項(xiàng)目建議書
- 2025年視聽周邊設(shè)備:耳機(jī)合作協(xié)議書
- 晚期肝癌護(hù)理措施
- 護(hù)理措施診斷
- 風(fēng)疹患者護(hù)理規(guī)范
- 公路水運(yùn)工程施工企業(yè)主要負(fù)責(zé)人和安全生產(chǎn)管理人員考核大綱和模擬試題庫1
- DL-T5024-2020電力工程地基處理技術(shù)規(guī)程
- 《鳳凰大視野》變局1962-七千人大會真相-(全集)
- 公園維修施工組織設(shè)計(jì)方案方案
- 樹立正確就業(yè)觀課件
- 2024年百聯(lián)集團(tuán)有限公司招聘筆試沖刺題(帶答案解析)
- ISO TR 15608-2017-中英文版完整
- 家政保潔培訓(xùn)課件
- 《在馬克思墓前的講話》課件+2023-2024學(xué)年統(tǒng)編版高中語文必修下冊
- 安防監(jiān)控系統(tǒng)維保表格完整
評論
0/150
提交評論