版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
1、新一代信息技術(shù)“十三五”系列規(guī)劃教材 Java 程序設(shè)計基礎(chǔ)教程(慕課版)劉剛 劉偉 編著第10章 并發(fā)編程 支持多線程是現(xiàn)代操作系統(tǒng)的一大特點,多線程的操作系統(tǒng)因為可以真正意義上地實現(xiàn)多任務(wù)同時運行,極大地提升了操作系統(tǒng)的處理速度。跨平臺的特性導(dǎo)致Java無法像C/C+這些語言一樣通過調(diào)用系統(tǒng)API來實現(xiàn)多線程程序,所以它在語言本身加了對多線程的支持。這些功能都以面向?qū)ο蟮姆绞絹韺崿F(xiàn),更加易于理解和使用。10.1 線程與進(jìn)程 在操作系統(tǒng)中,通常將進(jìn)程看作是系統(tǒng)資源分配和運行的基本單位,一個任務(wù)就是一個進(jìn)程。進(jìn)程擁有獨立的系統(tǒng)資源,包含CPU、內(nèi)存和輸入輸出端口等,例如打開的瀏覽器和Word文
2、檔,這些相對獨立的資源表明了進(jìn)程具有動態(tài)性、并發(fā)性、獨立性和異步性等特點。 線程(thread)是“進(jìn)程”中某個單一順序的控制流,被稱為輕量級進(jìn)程(lightweight processes),是比進(jìn)程更小的執(zhí)行單位,也是程序執(zhí)行流中最小的單位。一個標(biāo)準(zhǔn)的線程由線程ID、當(dāng)前指令指針(PC)、寄存器集合和堆棧組成。線程是進(jìn)程中的一個實體,是被系統(tǒng)獨立調(diào)度和分配的基本單位,線程在運行中的資源歸屬于進(jìn)程,同屬于一個進(jìn)程的所有線程共享該進(jìn)程所擁有的系統(tǒng)資源。 一個線程可以創(chuàng)建和撤銷另一個線程,同一個進(jìn)程中的多個線程也可以并發(fā)執(zhí)行。由于進(jìn)程所有資源是固定的且線程間存在相互制約,使得線程可能處于就緒、阻
3、塞和運行等狀態(tài),令線程的執(zhí)行呈現(xiàn)出間斷性。線程之間可以共享代碼和數(shù)據(jù)、實時通信、進(jìn)行必要的同步操作等。一個程序都至少擁有一個進(jìn)程;每個進(jìn)程擁有一個或者多個線程。每個線程都有自己獨立的資源和生命周期。 進(jìn)程和線程的最大區(qū)別在于,進(jìn)程是由操作系統(tǒng)來控制的,而線程則是由進(jìn)程來控制的。進(jìn)程都是相互獨立的,各自享有各自的內(nèi)存空間,因此進(jìn)程間的通信是昂貴且受限的,進(jìn)程間的轉(zhuǎn)換也是需要開銷的;線程則共享進(jìn)程的內(nèi)存空間,線程通信是便宜的且線程間的轉(zhuǎn)換也是低成本的,這種低成本低開銷的通信也可能會產(chǎn)生意想不到的錯誤:當(dāng)多個線程訪問同一個變量時,獲取到的值是不一樣的!不過,也不必?fù)?dān)心,這些問題可以通過同步機制和鎖機
4、制來消除。10.2 線程的創(chuàng)建 多線程技術(shù)是Java語言的重要特性之一,Java平臺提供了一套廣泛且功能強大的API、工具和技術(shù)。Java編寫的程序都運行在Java虛擬機(JVM)中。在JVM內(nèi)部,程序的多任務(wù)是通過線程來實現(xiàn)的。在同一個JVM進(jìn)程中,有且只有一個進(jìn)程,那就是JVM本身,在JVM環(huán)境中,所有的程序代碼都是以線程來運行的。 Java中的線程有兩種實現(xiàn)方式,一種是繼承Thread類,一種是實現(xiàn)Runnable接口。但是無論是哪種方式,線程都要使用到Thread類及其相關(guān)方法。10.2.1 繼承Thread類 Thread類是一個實體類,該類封裝了線程的行為,想要利用Thread創(chuàng)建
5、一個線程,必須創(chuàng)建一個從Thread類導(dǎo)出的子類,并實現(xiàn)Thread的run()方法,在run()方法內(nèi)部可以根據(jù)需要編寫相應(yīng)的實現(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)對象,它實現(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也提供了很多輔助方法,以讓線程正常運行和方便程序員對線程的控制,其常用方法如表10-2所示。方法名說明static int activeCount()返回線程組中正在運行的線程的數(shù)目void checkAccess()確定當(dāng)前運行的線程是否有權(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()如果類是使用單獨的Runnable對象構(gòu)造的,將調(diào)用Runnable對象的run()方法,否則本方法不做任何事情就返回了,如果是子類繼承Thread類,請務(wù)必實現(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)前線程,同時允許其他線程運行表10-2 常用的Thread方法 在以前的案例中,當(dāng)需要執(zhí)行當(dāng)前類時,每個類都有一個main()方法。該方法是類的入口,JVM會找到該入口方法并運行,此時產(chǎn)生了一個線程,該線程便是主線程。當(dāng)main()方法運行結(jié)束后,主線程運行完成,JVM也就隨即退出了。JVM負(fù)責(zé)對進(jìn)程、線程進(jìn)行管理,JVM分配時間片(CPU時間)給線程,線程按照系統(tǒng)的設(shè)定輪流獲取時間片執(zhí)行,切換時間很短,在對線程運行效率要求不嚴(yán)格的場景下可以忽略不計。案例10-1 Thread實現(xiàn)多線程 運行結(jié)果如圖10-1所示。圖10-1 運行結(jié)果 由于每個線程運行
11、的次數(shù)較少,所以線程默認(rèn)優(yōu)先級下的運行隨機性不是很明顯,但通過方框標(biāo)注的線程Thread-3的運行可以看出,實際上線程運行并不是順序的。案例10-2 Thread的部分方法使用 運行結(jié)果如圖10-2所示。圖10-2 運行結(jié)果案例10-3 start方法和run方法 運行結(jié)果如圖10-3所示。圖10-3 運行結(jié)果 啟動Thread類時,必須要使用start()方法啟動一個線程,如果直接調(diào)用run()方法,則JVM認(rèn)為這只是一次普通的方法調(diào)用,而非需要啟動一個線程在執(zhí)行run()方法內(nèi)部的邏輯。讀者在使用線程的時候切記。在start()方法調(diào)用后也可以看出,運行的是兩個線程的代碼,而且它們之間互不
12、干擾地同時執(zhí)行。所以一些工作交給線程去做的時候,啟動一個新線程的線程可以做自己想做的其他事情,而無需等到新線程的執(zhí)行結(jié)束。10.2.2 實現(xiàn)Runnable接口 實現(xiàn)多線程的另一個方式是實現(xiàn)Runnable接口。 Runnable只有一個方法,即run()方法,該方法需要由一個實現(xiàn)了此接口的類來實現(xiàn)。實現(xiàn)了Runnable接口的類的對象需要由Thread類的一個實例內(nèi)部運行它,其本身不能直接運行。案例10-4 Runnable實現(xiàn)多線程 運行結(jié)果如圖10-4所示。 圖10-4 運行結(jié)果 圖10-4只摘取部分的輸出內(nèi)容,從內(nèi)容上看,實現(xiàn)Runnable和繼承Thread都能達(dá)到相同目的,都能啟動
13、一個新線程。唯一的區(qū)別是Runnable對象必須包裝成Thread對象后才能運行。如果查看Thread和Runnable類源碼會發(fā)現(xiàn),Thread類實際上是Runnable的一個實現(xiàn)類??赡苡凶x者會對Runnable接口的存在產(chǎn)生疑問,畢竟這個接口只有一個run()方法。Runnable的存在是因為Java的類有且只能有一個直接父類,如果只是提供了Thread類,那么想要繼承其他類且需要同時繼承Thread類的這個子類,在實現(xiàn)這種繼承邏輯上會產(chǎn)生很多困難,而Runnable則避免了這種尷尬局面的出現(xiàn),在Java中,一個類是可以實現(xiàn)多個接口的。10.3 線程的調(diào)度 在JVM中,線程只有在獲取了C
14、PU分配的時間片后才會真正地執(zhí)行,在線程創(chuàng)建后到死亡的這個過程中還有其他的線程狀態(tài),這些狀態(tài)組成了線程的生命周期。 10.3.1 線程的生命周期 如同生命體一般,線程也有生命周期,線程的生命周期是從線程新建開始,一直持續(xù)到線程死亡。在新建和死亡之間,線程還有就緒、阻塞和運行狀態(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)的線程并不具備運行的能力,該操作對于系統(tǒng)而言,僅僅消耗普通對象創(chuàng)建時會消耗的非CPU資源。
15、 就緒:當(dāng)處于新建狀態(tài)的線程調(diào)用start()方法被啟動之后,線程將進(jìn)入線程隊列等待CPU時間片,進(jìn)行執(zhí)行。此時的線程才具備了運行的能力,一旦獲取了時間片線程就執(zhí)行。 運行:就緒狀態(tài)的線程獲取了時間片之后,就進(jìn)入了運行狀態(tài),此時線程會執(zhí)行run()方法內(nèi)的代碼邏輯。線程一旦進(jìn)入運行狀態(tài),就與啟動該線程的線程沒有任何關(guān)系了,兩者平行運行,互不影響。 阻塞:線程在運行的過程中因資源無法滿足、前驅(qū)任務(wù)沒有完成或者被調(diào)用阻塞方法都會導(dǎo)致線程進(jìn)入阻塞狀態(tài)。阻塞狀態(tài)的線程會讓出CPU,然后等待,直到引起阻塞的條件不存在了,線程會重新進(jìn)入就緒狀態(tài),等待CPU時間片。 死亡:不具備繼續(xù)運行能力的線程就處于死亡
16、狀態(tài)。線程在運行完畢后會自然進(jìn)入死亡狀態(tài)正常死亡,在運行過程中也會因為異常退出而導(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是運行在所屬系統(tǒng)上的一個線程,線程的創(chuàng)建和執(zhí)行還是需要基于對應(yīng)的系統(tǒng)的,所以,在一些不支持線程優(yōu)先級策略的系統(tǒng)中,Java設(shè)定的優(yōu)先級并不起作用
17、,這一點是讀者一定要引起注意的。案例10-5 線程優(yōu)先級 運行結(jié)果如圖10-6所示。 圖10-6 運行結(jié)果 運行結(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)先級,讀者需要注意以下幾點。(1)并不是線程優(yōu)先級高的線程一定會比線程優(yōu)先級低的線程先執(zhí)行,它只是會比線程優(yōu)先級低的線程有更多的機會先執(zhí)行。(2)Java的線程優(yōu)先級取決于JVM運行的系統(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 線程插隊 線程的魅力是充分地利用CPU,使得程序在單位時間內(nèi)充分地利用CPU而提升程序的處理效率。但由于線程運行順序的不確定性加上當(dāng)代操作系統(tǒng)核心數(shù)的提升,導(dǎo)致在某些情況下線程無法明確前驅(qū)任務(wù)是否完成。為了保證前驅(qū)任務(wù)完成后才執(zhí)行當(dāng)前線程,可以調(diào)用join()方法。join()會阻塞當(dāng)前線程直到插隊線程執(zhí)行完畢之后才會繼續(xù)執(zhí)行。案例10-6 線程插隊 運行結(jié)果如圖10-7所示。圖10-7 運行結(jié)果10.3.4 線程休眠 Thread類中有sleep()方法。該方法可以讓當(dāng)前線程休眠并讓出CPU,使得其他線程可
19、以獲取CPU進(jìn)行執(zhí)行。對于周期性很強的系統(tǒng),調(diào)用線程休眠是最好的形式,線程休眠時只會等待休眠結(jié)束且不占用CPU資源,等到線程休眠結(jié)束后會進(jìn)入就緒狀態(tài)等待時間片繼續(xù)執(zhí)行。案例10-7 線程休眠 運行結(jié)果如圖10-8所示。圖10-8 運行結(jié)果10.3.5 同步與互斥 寄宿學(xué)校都會有排隊打水的場景,許多人同時等待一個開水閥準(zhǔn)備接開水。當(dāng)前面一個人接水完畢后,后面一個人才能開始接水,如果接水的動作不是同步的,那么就會出現(xiàn)問題。案例10-8 非同步接水 運行結(jié)果如圖10-9所示。圖10-9 運行結(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)于修飾代碼塊,作用于類的一個實例,對另一個實例不起作用;對于靜態(tài)變量的修飾類似于靜態(tài)方法,作用于類的所有實例。案例10-9 同步接水 運行結(jié)果如圖10-10所示。圖10-10 運行結(jié)果 該案例使用的是synchronized修飾靜態(tài)成員變量的方式。使用該方式會對這個類的所有對象進(jìn)行同步控制,也就是說,每一次只會有一個該類的對象執(zhí)行synchronized修飾的代碼內(nèi)容,其他線程對該類的這個對象和該類的其他對象都必須等待當(dāng)前線程執(zhí)行完畢方可執(zhí)行。 有時候為了實現(xiàn)這種同步,也會使用信號量進(jìn)行控制,具體案例如下:案例10-10 線程互斥的計數(shù)器 運行結(jié)果如圖10-11所示。圖1
22、0-11 運行結(jié)果 其中flag相當(dāng)于一個信號量,當(dāng)有線程訪問公共資源的時候會首先檢測信號量,如果可用,則修改信號量防止其他線程進(jìn)入,否則就進(jìn)入等待,當(dāng)訪問完成之后修改信號量,并將所有處于該信號量等待狀態(tài)的線程喚醒,給其他線程獲取該信號量的機會。案例10-11 生產(chǎn)者-消費者模型 運行結(jié)果如圖10-12所示。 圖10-12 運行結(jié)果 生產(chǎn)-消費者模型是線程同步中最著名的同步問題,在該模型中,生產(chǎn)者負(fù)責(zé)生產(chǎn)數(shù)據(jù),但數(shù)據(jù)需要在可緩存的數(shù)量之內(nèi),如果超出庫存則需要等待數(shù)據(jù)被消費后再插入;消費者消費庫存數(shù)據(jù)則恰恰相反,如果庫存空了則需要等待,等到有庫存以后再進(jìn)行消費。從案例10-11中可以發(fā)現(xiàn),雖然消
23、費者和生產(chǎn)者在消費和生產(chǎn)的層面上是異步進(jìn)行的,但是他們之間必須保持同步,生產(chǎn)者不能在庫存滿了之后還繼續(xù)增加庫存,消費者也不能在一個空的庫存中獲取產(chǎn)品。10.3.6 死鎖問題 在日常生活中偶爾會碰到這種情況,買肉的說:“我只有拿到了肉我才會給賣肉的錢!”而賣肉的則說:“我只有拿到了錢才會給買肉的肉!”這種爭執(zhí)如果得不到勸和必然導(dǎo)致買肉的買不到肉,賣肉的賣不出去肉,這種“死腦筋”的場景在計算機系統(tǒng)中被稱為死鎖。 死鎖是指多個進(jìn)程因競爭資源而造成的一種相互等待的僵局,如果沒有外力的作用,必然導(dǎo)致無限的等待。例如,A進(jìn)程占用了輸入設(shè)備,在釋放前請求了打印機設(shè)備,但是打印機被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)程運行過程中請求和釋放資源的順序不當(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)程強行奪走,即只能由獲得該資源的進(jìn)程自己來釋放(只能是主動釋放)。 循環(huán)等待條
25、件:若干進(jìn)程間形成首尾相接循環(huán)等待資源的關(guān)系。 死鎖只能在上述4個條件都滿足的條件下才能產(chǎn)生。案例10-12 線程死鎖 運行結(jié)果如圖10-13所示。圖10-13 運行結(jié)果 這是比較簡單的競爭導(dǎo)致的死鎖,案例10-12中,線程t1獲得了一個對象鎖objALock,釋放前請求objBLock鎖,而t2線程則是獲取了objBLock鎖,釋放前請求objALock鎖,由于雙方都要求在獲取對方的鎖后釋放鎖,導(dǎo)致了類似于先給錢還是先給肉的矛盾而產(chǎn)生死鎖。 死鎖產(chǎn)生的條件有四個,所以想要避免死鎖,只需要破壞四個條件中的任意一個就能實現(xiàn)。例如:可以避免嵌套鎖,嵌套鎖是死鎖產(chǎn)生的高發(fā)場景;避免無限期等待,可以設(shè)
26、置等待超時時間;一次只對一個資源獲取鎖,當(dāng)需要獲取另一個鎖的時候,先釋放當(dāng)前鎖。10.4 多線程 理解了線程的創(chuàng)建、同步和死鎖問題之后,就是領(lǐng)會多線程真正魅力的時候了,相較于串行執(zhí)行的簡單和耗時,多線程則稍顯復(fù)雜且高效。軍事天才拿破侖可以同時聽取數(shù)位將軍的匯報并做出相應(yīng)的軍事部署,就是因為他具有多線程可以同時處理多個任務(wù)的能力。10.4.1 線程池技術(shù) Java中的線程池技術(shù)是運行場景最多的并發(fā)框架,幾乎所有需要異步或者并發(fā)執(zhí)行任務(wù)的程序都可以使用線程池技術(shù)。合理使用線程池技術(shù)可以降低線程創(chuàng)建和銷毀造成的消耗,提高相應(yīng)速度和提高線程的可管理性。 線程池的處理流程如下。(1)線程池判斷核心線程池
27、是否都在執(zhí)行任務(wù),如果不是,創(chuàng)建一個新的線程來執(zhí)行任務(wù),如果核心線程池里的線程都在執(zhí)行任務(wù),則進(jìn)入下一個流程。(2)線程池判斷工作隊列是否已經(jīng)滿了。如果沒有滿,將新提交的任務(wù)存儲到這個工作隊列中,如果滿了,則進(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ù),超出的線程會在隊列中等待。(3)newScheduledThreadPool:創(chuàng)建一個定長線程池,支持定時及周期性任務(wù)執(zhí)行。(4)newSingleThreadExecutor:創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任務(wù),保證所有的任務(wù)按照指定順序(FIFO、LIFO、優(yōu)先級)執(zhí)行。 緩存線程池使用得比較普遍,而計劃任務(wù)線程池的功能相對比較特殊,下面就對這兩個線程池做一個簡單的實例說明。案例10-13 緩存線程池 運行結(jié)果如圖10-14所示。圖10-14 運行結(jié)果 從案例運行可以看出,緩存線程池的線程在執(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)入等待隊列。案例10-14 計劃任務(wù)線程池 運行結(jié)果如圖10-15所示。 圖10-15 運行結(jié)果 案例使用的是固定周期執(zhí)行的計劃任務(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的用法 運行結(jié)果如圖10-16所示。 圖10-16 運行結(jié)果 Callable支持返回值,并可以被ExecutorService運行,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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年人教新起點七年級化學(xué)上冊階段測試試卷含答案
- 2025年新世紀(jì)版選擇性必修1物理上冊階段測試試卷含答案
- 2025至2030年中國高檔螺旋浴巾數(shù)據(jù)監(jiān)測研究報告
- 2025年外研版選擇性必修3語文上冊階段測試試卷含答案
- 電力設(shè)備物流協(xié)議樣本
- 商業(yè)用地居間合同模板
- 2025至2030年中國節(jié)能冰柜數(shù)據(jù)監(jiān)測研究報告
- 2025至2030年中國比薩鍋數(shù)據(jù)監(jiān)測研究報告
- 2025至2030年中國地球運行儀數(shù)據(jù)監(jiān)測研究報告
- 2025年人教版選修3物理下冊階段測試試卷含答案
- 骨科手術(shù)后患者營養(yǎng)情況及營養(yǎng)不良的原因分析,骨傷科論文
- GB/T 24474.1-2020乘運質(zhì)量測量第1部分:電梯
- GB/T 12684-2006工業(yè)硼化物分析方法
- 定崗定編定員實施方案(一)
- 高血壓患者用藥的注意事項講義課件
- 特種作業(yè)安全監(jiān)護(hù)人員培訓(xùn)課件
- (完整)第15章-合成生物學(xué)ppt
- 太平洋戰(zhàn)爭課件
- 封條模板A4打印版
- T∕CGCC 7-2017 焙烤食品用糖漿
- 貨代操作流程及規(guī)范
評論
0/150
提交評論