Java基礎(chǔ)實(shí)踐教程-多線程_第1頁(yè)
Java基礎(chǔ)實(shí)踐教程-多線程_第2頁(yè)
Java基礎(chǔ)實(shí)踐教程-多線程_第3頁(yè)
Java基礎(chǔ)實(shí)踐教程-多線程_第4頁(yè)
Java基礎(chǔ)實(shí)踐教程-多線程_第5頁(yè)
已閱讀5頁(yè),還剩19頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

11.1線程概述11.2線程的創(chuàng)建11.3線程的生命周期11.4線程的調(diào)度11.5多線程同步11.1線程概述在計(jì)算機(jī)里被啟動(dòng)的獨(dú)立運(yùn)行的程序叫作進(jìn)程(process)。單擊鍵盤(pán)的組合鍵“Ctrl+Alt+Delete”,進(jìn)入“任務(wù)管理器”,單擊“更多”選項(xiàng),就可以看到如圖11-1-1所示的任務(wù)管理器窗口。在“進(jìn)程”選項(xiàng)卡中可以看到當(dāng)前計(jì)算機(jī)系統(tǒng)運(yùn)行的程序,如瀏覽器、PPT和Word等,以及系統(tǒng)現(xiàn)在運(yùn)行的進(jìn)程,例如ACE2-Server等。這里需要注意的是,計(jì)算機(jī)程序與進(jìn)程是兩個(gè)不同的概念。計(jì)算機(jī)程序是一組計(jì)算機(jī)能識(shí)別和執(zhí)行的指令,運(yùn)行于計(jì)算機(jī)操作系統(tǒng)上,滿足人們某種需求的軟件工具,而進(jìn)程是一個(gè)程序的一次執(zhí)行過(guò)程,是系統(tǒng)運(yùn)行程序的基本單位。因此,進(jìn)程是程序運(yùn)行的基本單位。當(dāng)計(jì)算機(jī)開(kāi)啟一個(gè)進(jìn)程時(shí),就會(huì)將對(duì)應(yīng)的程序及相關(guān)的資源調(diào)配到內(nèi)存中,每個(gè)進(jìn)程在內(nèi)存中都有相對(duì)獨(dú)立的存儲(chǔ)單元。線程(Thread)是進(jìn)程中的一個(gè)執(zhí)行單元,也是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位。線程負(fù)責(zé)當(dāng)前進(jìn)程中程序的執(zhí)行,就好比社區(qū)醫(yī)院在為老年人提供體檢活動(dòng)時(shí),有專員做登記、有專員做血糖檢查、有專員做外科檢查等,這些專員就類似于一個(gè)個(gè)的線程。線程在內(nèi)存中共享一塊儲(chǔ)存空間,但相對(duì)獨(dú)立運(yùn)行。線程結(jié)束,進(jìn)程不一定結(jié)束;進(jìn)程結(jié)束,它所包含的所有線程都會(huì)結(jié)束。簡(jiǎn)而言之,一個(gè)程序運(yùn)行時(shí)至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程中至少有一個(gè)線程。在多任務(wù)操作系統(tǒng)中,如果計(jì)算機(jī)是單核CPU,所有線程(任務(wù))依次執(zhí)行,CPU完成一個(gè)任務(wù)再進(jìn)行下一個(gè)任務(wù),這種執(zhí)行方式叫作單線程。如果CPU在很小的時(shí)間間隔交替執(zhí)行多個(gè)線程(任務(wù)),任一個(gè)時(shí)刻點(diǎn)上只有一個(gè)線程在CPU上運(yùn)行,用戶只是感覺(jué)計(jì)算機(jī)在同時(shí)執(zhí)行多項(xiàng)任務(wù),就叫作多線程,也叫并發(fā)。如果計(jì)算機(jī)是多核CPU,那么一個(gè)時(shí)刻點(diǎn)上可以有多個(gè)線程在不同CPU上同時(shí)運(yùn)行。比如一個(gè)公司有多個(gè)員工,他們各司其職,這種執(zhí)行方式叫作并行。實(shí)際上,并行包含了并發(fā),多個(gè)CPU在并行運(yùn)行時(shí),每個(gè)CPU可以同時(shí)運(yùn)行多個(gè)線程(并發(fā))。對(duì)比單線程和多線程,它們各有優(yōu)劣。單從CPU的運(yùn)行效率上考慮,單任務(wù)進(jìn)程及單線程效率是最高的,因?yàn)镃PU沒(méi)有任何進(jìn)程及線程的切換開(kāi)銷。但對(duì)于一些耗時(shí)的任務(wù),比如下載視頻、播放音樂(lè)和等待用戶輸入文本等操作,CPU就會(huì)有很多空閑時(shí)間。因此,單線程存在CPU利用率不高的劣勢(shì),用戶體驗(yàn)差。而多線程能夠充分利用CPU,比如在等待用戶輸入文本的同時(shí),可以利用空閑時(shí)間來(lái)下載視頻、播放音樂(lè)等。因此,多線程的CPU利用率高,用戶體驗(yàn)好。然而由于多線程需要不斷地切換執(zhí)行的任務(wù),因此它的CPU運(yùn)行效率并不高。在實(shí)際應(yīng)用中多線程的應(yīng)用最為廣泛,絕大多數(shù)的多任務(wù)操作系統(tǒng)采用的都是多線程模式。需要注意的是,這里指的多線程不限于某個(gè)進(jìn)程中的若干線程,而是在計(jì)算機(jī)中所有進(jìn)程的線程。計(jì)算機(jī)的底層硬件和操作系統(tǒng)決定了這臺(tái)計(jì)算機(jī)是否可以支持多線程操作。當(dāng)一個(gè)程序中只有單個(gè)線程時(shí),它在CPU并發(fā)操作中與其他運(yùn)行程序的線程一起運(yùn)行,對(duì)計(jì)算機(jī)而言仍然是多線程操作。此時(shí)的程序可稱為單線程程序。在本書(shū)之前各章中編寫(xiě)的程序都是單線程程序,即按照main方法中編寫(xiě)的代碼順序執(zhí)行,當(dāng)main方法執(zhí)行完畢,整個(gè)程序也就運(yùn)行結(jié)束了。當(dāng)一個(gè)程序中定義了多個(gè)線程時(shí),該程序就是多線程程序,此時(shí)即使main方法執(zhí)行完畢,程序可能還在運(yùn)行。多線程程序在并發(fā)中可以同時(shí)執(zhí)行當(dāng)前進(jìn)程中的多個(gè)任務(wù),如圖11-1-2所示。電腦版微信可以同時(shí)進(jìn)行文字聊天(一對(duì)多)、視頻聊天和傳輸文件(一對(duì)多)等任務(wù)。它們之間能夠相對(duì)獨(dú)立運(yùn)行,開(kāi)啟或關(guān)閉某一個(gè)線程對(duì)其他的線程都不產(chǎn)生影響,對(duì)用戶而言非常方便。在Java語(yǔ)言中提供了java.lang.Thread類、java.lang.Runnable接口和java.lang.callable接口,以及類內(nèi)繼承和定義的方法,以支持設(shè)計(jì)Java多線程程序。11.2線程的創(chuàng)建11.2.1線程的創(chuàng)建和運(yùn)行方法Java語(yǔ)言中的線程使用Thread類來(lái)描述。Thread類直接繼承自O(shè)bject類。在Java單線程程序中,Java程序在運(yùn)行main方法時(shí)就創(chuàng)建了名為main的線程,即主線程。主線程被放在名為main的線程組中,默認(rèn)優(yōu)先級(jí)為5??梢酝ㄟ^(guò)Thread類中的一些方法查詢和設(shè)置線程的屬性,如表11-2-1所示。對(duì)于單線程程序,當(dāng)main程序執(zhí)行完之后,主線程也就執(zhí)行完畢,程序也隨之執(zhí)行完畢。在多線程程序中,只能有一個(gè)主線程。除了主線程外編程人員還可以自定義線程,稱之為子線程。當(dāng)所有的線程都執(zhí)行完,或者在代碼中直接調(diào)用了System.exit(0),即退出JVM,則程序執(zhí)行結(jié)束。Thread類還實(shí)現(xiàn)了Runnable接口。Runnable接口中僅存在一個(gè)抽象方法run,Thread類中實(shí)現(xiàn)了run方法,但只是通過(guò)多態(tài)的形式調(diào)用了父類的run方法,并沒(méi)有其他邏輯功能,其源代碼如下:其中,target變量即是Runnable接口的對(duì)象引用,其源代碼如下:創(chuàng)建一個(gè)線程,就是創(chuàng)建一個(gè)線程類的對(duì)象。運(yùn)行一個(gè)線程,就是使用線程類的對(duì)象調(diào)用Thread類的start方法。此時(shí),JVM會(huì)將該線程加入程序的線程組中,并運(yùn)行該線程的run方法。當(dāng)run方法執(zhí)行完畢時(shí),該線程也就執(zhí)行完畢。因此,在創(chuàng)建線程時(shí),就需要重寫(xiě)run方法,將線程要執(zhí)行的功能代碼寫(xiě)在run方法中。在JDK17中,Thread類提供了九種重載的構(gòu)造方法,表10-2-2給出了四種常用的構(gòu)造方法。由此可知,創(chuàng)建并運(yùn)行一個(gè)線程可以通過(guò)兩種方式:一種是自定義一個(gè)類繼承Thread類,重寫(xiě)run方法,然后使用該類的對(duì)象調(diào)用start方法即可;另一種是自定義一個(gè)類實(shí)現(xiàn)Runnable接口,實(shí)現(xiàn)run方法,然后將該類的對(duì)象作為形參傳入Thread類的構(gòu)造方法中,使用實(shí)例化的線程對(duì)象調(diào)用start方法即可。在JDK1.5之后,官方又提供了FutureTask類和Callable接口來(lái)創(chuàng)建線程。其中,Callable接口作為FutureTask構(gòu)造方法的形參,而FutureTask類又實(shí)現(xiàn)了Runnable接口,因此它本質(zhì)上仍屬于第二種構(gòu)建線程的方式。由于FutureTask類和Callable接口支持泛型,并且Callable的運(yùn)行方法call具有返回值,因此通常將它們創(chuàng)建線程的方式單獨(dú)列出來(lái)作為第三種形式。接下來(lái)分別介紹這三種創(chuàng)建線程的方法。11.2.2繼承Thread類創(chuàng)建線程該方法創(chuàng)建和運(yùn)行線程的步驟如下:(1)自定義一個(gè)類,繼承Thread類;(2)重寫(xiě)run方法,將線程邏輯放在run方法中;(3)創(chuàng)建該類的對(duì)象,在其他線程中調(diào)用start方法開(kāi)啟該線程。若創(chuàng)建了多個(gè)該類的對(duì)象并各自調(diào)用了start方法,則開(kāi)啟多個(gè)子線程。11.2.3實(shí)現(xiàn)Runnable接口創(chuàng)建線程通過(guò)這種形式創(chuàng)建并運(yùn)行線程時(shí),它的基本步驟如下:(1)自定義一個(gè)類實(shí)現(xiàn)Runnable接口,實(shí)現(xiàn)它的抽象方法run方法。(2)在其他線程中創(chuàng)建該類的對(duì)象,并作為形參傳入Thread構(gòu)造方法中。(3)使用Thread類的對(duì)象調(diào)用start方法,開(kāi)啟線程。11.2.4使用Callable和FutureTask創(chuàng)建線程使用Callable和FutureTask創(chuàng)建并運(yùn)行線程的步驟如下:(1)自定義一個(gè)類實(shí)現(xiàn)Callable接口,實(shí)現(xiàn)它的call方法。(2)創(chuàng)建FutureTask類的對(duì)象,將Callable接口的實(shí)例類對(duì)象傳進(jìn)去。(3)創(chuàng)建線程對(duì)象,將FutureTask類的對(duì)象作為Runnable接口的對(duì)象引用傳進(jìn)去。(4)使用線程對(duì)象調(diào)用start方法,運(yùn)行線程。(5)線程運(yùn)行完畢后,返回值由FutureTask類的對(duì)象接收。11.3線程的生命周期在計(jì)算機(jī)中,同時(shí)會(huì)有多個(gè)線程來(lái)爭(zhēng)奪CPU的使用權(quán),線程在此過(guò)程中的狀態(tài)會(huì)頻繁地切換。為了能夠清楚地描述線程的狀態(tài),方便線程管理,定義了線程的生命周期。線程的生命周期分為五個(gè)狀態(tài),即新建狀態(tài)(New)、就緒狀態(tài)(Runnable)、運(yùn)行狀態(tài)(Running)、阻塞狀態(tài)(Blocked)和死亡狀態(tài)(Terminated),如圖11-3-1所示。線程生命周期的五個(gè)狀態(tài)的內(nèi)涵如下:(1)新建狀態(tài)對(duì)應(yīng)的是線程對(duì)象被創(chuàng)建好,但還沒(méi)有調(diào)用start方法。此時(shí)對(duì)象只有自己的數(shù)據(jù)空間,還沒(méi)有加入線程組,沒(méi)有開(kāi)始運(yùn)行。(2)就緒狀態(tài)對(duì)應(yīng)的是線程對(duì)象已經(jīng)調(diào)用了start方法,并且已經(jīng)被加入到了線程組,具備爭(zhēng)奪CPU使用權(quán)的資格,但目前沒(méi)有在運(yùn)行。(3)運(yùn)行狀態(tài)對(duì)應(yīng)的是線程爭(zhēng)奪到了CPU,正在執(zhí)行它run或者call方法里面的代碼。(4)阻塞狀態(tài)對(duì)應(yīng)的是線程被暫時(shí)取消了爭(zhēng)奪CPU使用權(quán)的資格,暫時(shí)不可以運(yùn)行。(5)死亡狀態(tài)指的是線程執(zhí)行完畢,或者執(zhí)行期間拋出一個(gè)沒(méi)有捕獲的異?;蝈e(cuò)誤。線程就此終止,它的生命周期也就結(jié)束了。這五個(gè)狀態(tài)并非獨(dú)立存在,而是存在著聯(lián)系。圖中的箭頭指向表明了線程可以從某個(gè)狀態(tài)轉(zhuǎn)換到另一種狀態(tài)。線程狀態(tài)之間的轉(zhuǎn)換包括以下幾種情況:(1)新建狀態(tài)可以轉(zhuǎn)換成就緒狀態(tài);(2)就緒狀態(tài)可以與運(yùn)行狀態(tài)相互轉(zhuǎn)換;(3)運(yùn)行狀態(tài)可以轉(zhuǎn)換成阻塞狀態(tài)和死亡狀態(tài);(4)阻塞狀態(tài)可以轉(zhuǎn)換成就緒狀態(tài)。這里需要注意的是,其他情況下?tīng)顟B(tài)是不可進(jìn)行轉(zhuǎn)換的。比如,就緒狀態(tài)不能直接轉(zhuǎn)換成阻塞狀態(tài)。每一種狀態(tài)轉(zhuǎn)換都是需要條件的。比如,新建狀態(tài)轉(zhuǎn)換成就緒狀態(tài)需要線程對(duì)象調(diào)用start方法;運(yùn)行狀態(tài)轉(zhuǎn)換成死亡狀態(tài)需要線程執(zhí)行完畢,或者拋出一個(gè)沒(méi)有捕獲的異常或錯(cuò)誤。其中,大多數(shù)線程會(huì)在就緒狀態(tài)、運(yùn)行狀態(tài)和阻塞狀態(tài)之間頻繁地切換。下面針對(duì)這幾種狀態(tài)及其轉(zhuǎn)換進(jìn)行說(shuō)明。1.就緒狀態(tài)到運(yùn)行狀態(tài)的轉(zhuǎn)換就緒狀態(tài)到運(yùn)行狀態(tài)轉(zhuǎn)換的條件是線程爭(zhēng)奪到了CPU的使用權(quán),CPU會(huì)分配一定的時(shí)間片去執(zhí)行當(dāng)前線程中的方法。2.運(yùn)行狀態(tài)到其他狀態(tài)的轉(zhuǎn)換線程在運(yùn)行狀態(tài)下,可能出現(xiàn)以下三種情況:(1)運(yùn)行狀態(tài)到就緒狀態(tài)。線程代碼在給定的時(shí)間內(nèi)沒(méi)有執(zhí)行完畢,或者在代碼中執(zhí)行了線程讓步方法yield,則線程就會(huì)自動(dòng)轉(zhuǎn)換成就緒狀態(tài),開(kāi)始下一次CPU的爭(zhēng)奪。有可能還會(huì)爭(zhēng)奪到CPU繼續(xù)執(zhí)行,也有可能沒(méi)有爭(zhēng)奪到。(2)運(yùn)行狀態(tài)到死亡狀態(tài)。線程代碼在給定的時(shí)間內(nèi)執(zhí)行完畢了,該線程直接進(jìn)入死亡狀態(tài),不復(fù)存在。(3)運(yùn)行狀態(tài)到阻塞狀態(tài)。線程代碼在執(zhí)行過(guò)程中需要等待讀寫(xiě)操作,或者執(zhí)行了線程阻塞方法,包括join、sleep和wait方法,或者遇到了同步鎖被占用的情況,則線程進(jìn)入阻塞狀態(tài)。3.阻塞狀態(tài)到就緒狀態(tài)的轉(zhuǎn)換當(dāng)線程處于阻塞狀態(tài)時(shí),需要出現(xiàn)一定的條件才可以轉(zhuǎn)換為就緒狀態(tài)。所謂“解鈴還須系鈴人”,根據(jù)線程進(jìn)入阻塞狀態(tài)原因的不同,線程從阻塞狀態(tài)返回到就緒狀態(tài),可分為以下幾種情況:(1)等待讀寫(xiě)操作方法返回。如果線程是由于等待讀寫(xiě)操作進(jìn)入阻塞狀態(tài),那么只有當(dāng)阻塞它的讀寫(xiě)操作方法返回時(shí)才可以回到就緒狀態(tài)。(2)等待線程阻塞方法join。如果線程是由于執(zhí)行了線程阻塞方法join進(jìn)入阻塞狀態(tài),那么只有等待調(diào)用join方法的線程執(zhí)行完畢,或者超過(guò)等待時(shí)間才可以回到就緒狀態(tài);(3)等待線程休眠方法sleep。如果線程因執(zhí)行了線程休眠方法sleep進(jìn)入阻塞狀態(tài),那么必須等待該線程休眠時(shí)間過(guò)了才可以回到就緒狀態(tài)。(4)執(zhí)行等待方法wait。如果線程因執(zhí)行了線程等待方法wait進(jìn)入阻塞狀態(tài),那么必須等待其他的線程使用notify方法喚醒后才可以回到就緒狀態(tài)。11.4線程的調(diào)度Java語(yǔ)言中的線程調(diào)度就是通過(guò)Thread類提供的操作方法對(duì)線程的狀態(tài)進(jìn)行管理,控制線程按照給定的邏輯順序執(zhí)行代碼。除了setter和getter方法之外,表11-4-1所示的Thread類中的方法也比較常用。11.4.1線程優(yōu)先級(jí)線程的優(yōu)先級(jí)決定了線程在爭(zhēng)奪CPU時(shí)的概率。線程的優(yōu)先級(jí)越高,它獲得CPU執(zhí)行權(quán)的概率越大。需要注意的是,在一定時(shí)間內(nèi),優(yōu)先級(jí)高的線程的執(zhí)行次數(shù)不一定比優(yōu)先級(jí)低的線程的執(zhí)行次數(shù)多。在Java語(yǔ)言中,線程的優(yōu)先級(jí)使用1~10之間的整數(shù)來(lái)表示,數(shù)字越大優(yōu)先級(jí)越高。在創(chuàng)建一個(gè)線程時(shí),如果沒(méi)有指定它的優(yōu)先級(jí),默認(rèn)的優(yōu)先級(jí)為5,對(duì)應(yīng)Thread類中的NORM_PRIORITY靜態(tài)常量。此外,最低優(yōu)先級(jí)(1)對(duì)應(yīng)的靜態(tài)常量為T(mén)hread.MIN_PRIORITY,最高優(yōu)先級(jí)(10)對(duì)應(yīng)的靜態(tài)常量為T(mén)hread.MAX_PRIORITY。編程人員可以通過(guò)getPriority和setPriority方法來(lái)獲取和設(shè)置線程的優(yōu)先級(jí)。11.4.2線程休眠線程休眠就是讓當(dāng)前執(zhí)行Thread.sleep方法的線程進(jìn)入阻塞狀態(tài),直到過(guò)了休眠時(shí)間。這里的休眠方法有毫秒級(jí)和納秒級(jí)兩種。由于普通個(gè)人計(jì)算機(jī)的定時(shí)精度在毫秒級(jí),這里僅演示毫秒級(jí)的線程休眠。11.4.3線程讓步線程讓步就是當(dāng)前搶占到CPU的線程代碼讓出CPU的執(zhí)行權(quán),這個(gè)線程不再往下執(zhí)行,回到就緒狀態(tài),繼續(xù)下一輪的CPU爭(zhēng)奪賽。這里需要注意的是,線程讓步后,若再搶到CPU的執(zhí)行權(quán),將從讓步方法之后繼續(xù)執(zhí)行。線程讓步使用的是Thread類的yield靜態(tài)方法。11.4.4線程插隊(duì)線程插隊(duì)是在線程a的run方式中執(zhí)行線程b的join()方法,此時(shí)線程a進(jìn)入阻塞狀態(tài),直到線程b全部執(zhí)行完以后,或者超過(guò)指定的等待時(shí)間,線程a才會(huì)結(jié)束阻塞狀態(tài)。需要注意的是,這里所描述的插隊(duì)跟現(xiàn)實(shí)生活中排隊(duì)買(mǎi)東西時(shí)的插隊(duì)不一樣。線程插隊(duì)僅是b插隊(duì)到了a前面,而不是插隊(duì)到了所有線程之前。線程b仍然需要跟其他的線程爭(zhēng)奪CPU的使用權(quán),直到b執(zhí)行完畢,或者超過(guò)等待時(shí)間,線程a才回到就緒狀態(tài),參與CPU的爭(zhēng)奪。線程插隊(duì)是通過(guò)join方法實(shí)現(xiàn)的。11.5多線程同步11.5.1同步代碼塊和同

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論