Java基礎(chǔ)案例教程(第3版) 課件 第8章 多線程_第1頁
Java基礎(chǔ)案例教程(第3版) 課件 第8章 多線程_第2頁
Java基礎(chǔ)案例教程(第3版) 課件 第8章 多線程_第3頁
Java基礎(chǔ)案例教程(第3版) 課件 第8章 多線程_第4頁
Java基礎(chǔ)案例教程(第3版) 課件 第8章 多線程_第5頁
已閱讀5頁,還剩94頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

第8章多線程《Java基礎(chǔ)案例教程》(第3版)學(xué)習(xí)目標(biāo)/Target了解線程的創(chuàng)建和啟動,能夠簡述3種創(chuàng)建和啟動線程的方式熟悉線程的生命周期及狀態(tài)轉(zhuǎn)換,能夠簡述線程生命周期中的六種基本狀態(tài)和它們之間相互轉(zhuǎn)換的方式熟悉線程安全問題,能夠簡述多線程程序出現(xiàn)線程安全問題的原因了解線程和進程,能夠簡述進程與線程的概念了解線程同步,能夠簡述實現(xiàn)線程同步的3種方式學(xué)習(xí)目標(biāo)/Target掌握線程同步的方式,能夠使用同步代碼塊、同步方法、Lock鎖這3種方式同步線程了解線程池,能夠簡述線程池的概念以及優(yōu)點熟悉線程池的使用,能夠使用ThreadPoolExecutor創(chuàng)建線程池對象,并使用線程池對象執(zhí)行線程任務(wù)熟悉線程的控制,能夠通過線程優(yōu)先級、線程休眠、線程讓步和線程插隊實現(xiàn)對線程的控制掌握線程的創(chuàng)建和啟動,能夠使用繼承Thread類、實現(xiàn)Runnable接口、基于Callable接口和FutureTask類這3種方式創(chuàng)建線程章節(jié)概述/

Summary日常生活中,許多事情都是并行執(zhí)行的。例如,人可以同時進行呼吸、血液循環(huán)、思考問題等活動。在一個應(yīng)用程序中也可以同時存在多個執(zhí)行路徑,每個執(zhí)行路徑獨立執(zhí)行不同的任務(wù)或代碼塊,這種有多條并發(fā)執(zhí)行路徑的程序,稱為多線程程序。本章將對多線程的相關(guān)知識進行詳細講解。目錄/Contents0102線程與進程線程的創(chuàng)建和啟動03線程的生命周期及狀態(tài)轉(zhuǎn)換目錄/Contents0405線程同步線程的控制06線程池線程與進程8.18.1線程與進程了解線程和進程,能夠簡述進程與線程的概念

先定一個小目標(biāo)!8.1線程與進程一臺計算機可以同時運行多個程序,每個運行中的程序都是一個進程,每個進程擁有自己的資源和狀態(tài),具有一個相對獨立的執(zhí)行環(huán)境。進程是程序的實例,是操作系統(tǒng)動態(tài)執(zhí)行的基本單元。雖然計算機允許多個程序同時運行,但實際上計算機中單核的CPU同一時刻只能處理一個進程,用戶之所以認為同時會有多個進程在運行,是因為計算機系統(tǒng)采用了“多道程序設(shè)計”技術(shù)。8.1線程與進程多道程序設(shè)計,是指計算機允許多個相互獨立的程序同時進入內(nèi)存,并相互穿插運行。多道程序技術(shù)將CPU的整個生命周期劃分為相同的時間片,每個CPU時間片內(nèi)只處理一個進程,但由于CPU劃分的時間片很微小,且運行速度快,所以宏觀上表現(xiàn)為計算機可以同時運行多個程序,處理多個進程。8.1線程與進程進程的實質(zhì)是程序在多道程序系統(tǒng)的一次執(zhí)行過程,但進程的實際運作單位是線程。線程是操作系統(tǒng)能夠進行運算調(diào)度的最小單位,它被包含在進程之中。每個進程中至少存在一個線程,每一個線程都是進程中一個單一順序的控制流。8.1線程與進程一個進程中的多個線程可以并行執(zhí)行不同的任務(wù)。在前面章節(jié)的程序中,代碼都是按照調(diào)用順序依次執(zhí)行,沒有出現(xiàn)兩段程序代碼交替運行的效果,這樣的程序稱作單線程程序。如果希望程序中實現(xiàn)多段程序代碼交替運行的效果,則需要在程序中創(chuàng)建多個線程,即多線程程序。一個多線程程序在執(zhí)行過程中會產(chǎn)生多個線程,這些線程可以并發(fā)執(zhí)行并相互獨立,其執(zhí)行過程的示意圖如下圖所示。線程的創(chuàng)建和啟動8.28.2線程的創(chuàng)建和啟動掌握線程的創(chuàng)建和啟動,能夠使用繼承Thread類、實現(xiàn)Runnable接口、基于Callable接口和FutureTask類這3種方式創(chuàng)建線程

先定一個小目標(biāo)!8.2線程的創(chuàng)建和啟動要編寫多線程程序,需要在程序中創(chuàng)建并啟動除主線程之外的其他線程。Java中使用Thread類來代表線程,所有的線程對象都必須是Thread類或其子類的實例。Thread類提供給了用于分配線程對象的構(gòu)造方法,常用構(gòu)造方法如下。方法聲明說明Thread()創(chuàng)建一個線程對象Thread(Stringname)創(chuàng)建一個名稱為name的線程對象Thread(Runnabletarget)根據(jù)任務(wù)對象target創(chuàng)建一個線程對象Thread(Runnabletarget,Stringname)根據(jù)任務(wù)對象target創(chuàng)建一個線程對象,并指定線程對象的名稱為name如果未指定線程名字,程序會自動為線程分配格式為“Thread-n”的名稱,n為非負整數(shù)的數(shù)字。8.2線程的創(chuàng)建和啟動Thread類提供多種方法用于操作線程,其中常用方法如下表所示。方法聲明說明StringgetName()獲取當(dāng)前線程的名稱voidsetName(Stringname)將當(dāng)前線程的名稱更改為參數(shù)namevoidstart()啟動一個新線程voidrun()線程的執(zhí)行方法,是線程執(zhí)行的主體staticvoidsleep(longmillis)使當(dāng)前正在執(zhí)行的線程休眠millis毫秒staticThreadcurrentThread()返回對當(dāng)前正在執(zhí)行的線程對象的引用8.2線程的創(chuàng)建和啟動Thread類用于操作線程的方法,在使用時的注意事項。start()方法用于啟動一個新線程,但是該方法本身并不是執(zhí)行線程代碼的方法。start()方法只是告訴JVM該線程已準(zhǔn)備好被執(zhí)行,并由JVM來負責(zé)調(diào)度和執(zhí)行。當(dāng)線程獲得了CPU時間片后,就會調(diào)用該線程的run()方法,run()方法是線程的執(zhí)行方法,其中包含線程的主要執(zhí)行邏輯。如果直接調(diào)用run()方法,則該方法并不會啟動線程,只是在當(dāng)前線程中執(zhí)行了run()方法中的代碼而已。8.2線程的創(chuàng)建和啟動Java提供了多種創(chuàng)建線程的方式,其中常用的創(chuàng)建方式有繼承Thread類、實現(xiàn)Runnable接口、基于Callable接口和FutureTask類實現(xiàn),下面分別對這三種創(chuàng)建方式進行講解。8.2線程的創(chuàng)建和啟動1.繼承Thread類創(chuàng)建線程通過繼承Thread類創(chuàng)建線程的步驟如下。(1)定義Thread類的子類,并重寫Thread類的run()方法,在run()方法中定義線程要執(zhí)行的任務(wù)。(2)創(chuàng)建Thread子類的實例,即創(chuàng)建線程對象。(3)調(diào)用線程對象的start()方法啟動線程。8.2線程的創(chuàng)建和啟動1.繼承Thread類創(chuàng)建線程下面通過一個案例演示如何通過繼承Thread類的方法創(chuàng)建和啟動線程。(1)定義線程類定義MyThread01類繼承Thread類,并重寫Thread類的run()方法,在run()方法中循環(huán)輸出當(dāng)前執(zhí)行線程的線程對象名稱。MyThread01.java源代碼8.2線程的創(chuàng)建和啟動1.繼承Thread類創(chuàng)建線程(2)啟動線程定義測試類Example01

,在類中的main()方法內(nèi)創(chuàng)建兩個MyThread01線程對象,并啟動線程。Example01.java源代碼8.2線程的創(chuàng)建和啟動1.繼承Thread類創(chuàng)建線程(3)測試效果運行文件Example01.java,運行效果如下圖所示。8.2線程的創(chuàng)建和啟動2.實現(xiàn)Runnable接口創(chuàng)建線程由于Java只支持單繼承,所以如果一個類繼承了某個父類之后,就無法再通過繼承Thread類創(chuàng)建線程。為了解決這個問題,可以借助Thread類提供的Thread(Runnabletarget)或Thread(Runnabletarget,Stringname)構(gòu)造方法來創(chuàng)建線程。其中Runnable接口的實例代表一個需要執(zhí)行的具體任務(wù),通常被稱為任務(wù)對象。Runnable接口只包含一個run()方法,用于定義線程的執(zhí)行代碼。8.2線程的創(chuàng)建和啟動2.實現(xiàn)Runnable接口創(chuàng)建線程通過實現(xiàn)Runnable接口創(chuàng)建多線程的步驟如下。(1)定義Runnable接口的實現(xiàn)類,并重寫該接口的run()方法。(2)創(chuàng)建Runnable實現(xiàn)類的實例,并將實例作為Thread的任務(wù)對象創(chuàng)建Thread對象。(3)調(diào)用線程對象的start()方法啟動線程。8.2線程的創(chuàng)建和啟動2.實現(xiàn)Runnable接口創(chuàng)建線程下面通過一個案例演示如何通過實現(xiàn)Runnable接口創(chuàng)建和啟動線程。(1)定義Runnable接口的實現(xiàn)類定義類MyRunnable01實現(xiàn)Runnable接口,并重寫接口中的run()方法。MyRunnable01.java源代碼8.2線程的創(chuàng)建和啟動2.實現(xiàn)Runnable接口創(chuàng)建線程(2)啟動線程定義測試類Example02

,在類中的main()方法內(nèi)創(chuàng)建1個MyRunnable01線程任務(wù)對象和2個Thread線程對象,并啟動線程。Example02.java源代碼8.2線程的創(chuàng)建和啟動2.實現(xiàn)Runnable接口創(chuàng)建線程(3)測試效果運行文件Example02.java文件,效果如下圖所示。8.2線程的創(chuàng)建和啟動3.基于Callable接口和FutureTask類創(chuàng)建線程通過實現(xiàn)Runnable接口創(chuàng)建線程時,將重寫的run()方法包裝成線程執(zhí)行體。由于run()方法沒有返回值,因此如果線程執(zhí)行結(jié)果需要返回時,實現(xiàn)Runnable接口的方式就太不合適了。為此,Java提供了Callable接口,該接口的call()方法可以作為線程的執(zhí)行體,并且有返回值。8.2線程的創(chuàng)建和啟動3.基于Callable接口和FutureTask類創(chuàng)建線程Callable對象不能直接作為Thread構(gòu)造方法的target參數(shù),需要借助FutureTask類創(chuàng)建對應(yīng)的參數(shù)對象,F(xiàn)utureTask類提供的構(gòu)造方法可以傳入一個Callable對象將其封裝起來,在創(chuàng)建Thread對象時,可以將FutureTask對象作為Thread的target參數(shù)傳入。當(dāng)線程執(zhí)行時,F(xiàn)utureTask內(nèi)部會執(zhí)行Callable對象的call()方法,并將結(jié)果存儲在FutureTask中。8.2線程的創(chuàng)建和啟動3.基于Callable接口和FutureTask類創(chuàng)建線程FutureTask類的繼承關(guān)系如下圖所示。8.2線程的創(chuàng)建和啟動3.基于Callable接口和FutureTask類創(chuàng)建線程FutureTask是Runnable接口和Future接口的間接實現(xiàn)類。其中,F(xiàn)uture接口用于管理線程返回結(jié)果,它共有5個方法,具體如下表所示。方法聲明功能描述booleancancel(booleanmayInterruptIfRunning)用于嘗試取消任務(wù),參數(shù)mayInterruptIfRunning表示是否允許取消正在執(zhí)行卻沒有執(zhí)行完畢的任務(wù),如果設(shè)置true,則表示可以取消正在執(zhí)行的任務(wù)booleanisCancelled()判斷任務(wù)是否被取消,如果被取消返回true,否則返回falsebooleanisDone()判斷任務(wù)是否已經(jīng)完成,如果任務(wù)完成返回true,否則返回falseVget()用于獲取執(zhí)行結(jié)果,這個方法會發(fā)生阻塞,一直等到任務(wù)執(zhí)行完畢才返回執(zhí)行結(jié)果Vget(longtimeout,TimeUnitunit)用于在指定時間內(nèi)獲取執(zhí)行結(jié)果,如果在指定時間內(nèi)無法獲取到結(jié)果則拋出異常8.2線程的創(chuàng)建和啟動3.基于Callable接口和FutureTask類創(chuàng)建線程基于Callable和FutureTask創(chuàng)建線程的步驟如下。(1)創(chuàng)建Callable接口的實現(xiàn)類。(2)創(chuàng)建包裝了Callable對象的FutureTask類。(3)使用FutureTask對象作為Thread對象的target參數(shù)并啟動線程。8.2線程的創(chuàng)建和啟動3.基于Callable接口和FutureTask類創(chuàng)建線程下面通過一個案例演示如何基于Callable和FutureTask創(chuàng)建和啟動線程。(1)定義Callable接口實現(xiàn)類定義MyCallable01類實現(xiàn)Callable接口,并重寫Callable接口的call()方法。Callable接口是泛型接口,可以在泛型中定義call()方法的返回值類型。MyCallable01.java源代碼8.2線程的創(chuàng)建和啟動3.基于Callable接口和FutureTask類創(chuàng)建線程(2)啟動線程定義測試類Example03,在類中的main()方法內(nèi)創(chuàng)建Callable線程任務(wù)對象,并通過FutureTask對象將線程任務(wù)對象進行封裝,將FutureTask對象作為Thread線程對象的target參數(shù)進行創(chuàng)建線程對象,并啟動線程。Example03.java源代碼8.2線程的創(chuàng)建和啟動3.基于Callable接口和FutureTask類創(chuàng)建線程(3)測試效果運行文件Example03.java,效果如下圖所示。8.2線程的創(chuàng)建和啟動繼承Thread類、實現(xiàn)Runnable接口、基于Callable和FutureTask三種創(chuàng)建線程方式的優(yōu)缺點。創(chuàng)建方式優(yōu)點缺點繼承Thread類編碼相對簡單存在單繼承的局限性,不便于擴展不能返回線程執(zhí)行的結(jié)果每個線程的任務(wù)和線程本身是耦合的,無法將任務(wù)和線程分離實現(xiàn)Runnable接口可以繼續(xù)繼承類和實現(xiàn)接口,擴展性強任務(wù)和線程分離,可以實現(xiàn)多個線程共享同一個任務(wù)編程相對復(fù)雜,不能返回線程執(zhí)行的結(jié)果基于Callable和FutureTask線程任務(wù)類只是實現(xiàn)接口,可以繼續(xù)繼承類和實現(xiàn)接口,擴展性強??梢栽诰€程執(zhí)行完畢后去獲取線程執(zhí)行的結(jié)果。編碼相對復(fù)雜線程的生命周期及狀態(tài)轉(zhuǎn)換8.38.3線程的生命周期及狀態(tài)轉(zhuǎn)換熟悉線程的生命周期及狀態(tài)轉(zhuǎn)換,能夠簡述線程生命周期中的六種基本狀態(tài)和它們之間相互轉(zhuǎn)換的方式

先定一個小目標(biāo)!8.3線程的生命周期及狀態(tài)轉(zhuǎn)換無限等待狀態(tài)被終止?fàn)顟B(tài)計時等待狀態(tài)新建狀態(tài)阻塞狀態(tài)可運行狀態(tài)8.3線程的生命周期及狀態(tài)轉(zhuǎn)換(1)新建狀態(tài)創(chuàng)建一個線程對象,但還沒調(diào)用start()方法啟動線程,該線程就處于新建狀態(tài)。此時,新建狀態(tài)的線程和其他聲明但未賦值的Java對象類似,JVM僅為其分配了內(nèi)存,并沒有表現(xiàn)出任何線程的動態(tài)特征。8.3線程的生命周期及狀態(tài)轉(zhuǎn)換(2)可運行狀態(tài)當(dāng)線程對象調(diào)用了start()方法后就進入可運行狀態(tài),也稱為就緒狀態(tài)。處于可運行狀態(tài)的線程位于線程隊列中,此時它只是具備了運行的條件,要獲得CPU的使用權(quán)并開始運行,還需要等待系統(tǒng)的調(diào)度。8.3線程的生命周期及狀態(tài)轉(zhuǎn)換(3)阻塞狀態(tài)如果處于可運行狀態(tài)的線程獲得了CPU的使用權(quán),并開始執(zhí)行run()方法中的線程執(zhí)行體,則線程處于運行狀態(tài)。一個線程啟動后,它可能不會一直處于運行狀態(tài),當(dāng)一個線程試圖獲取一個對象鎖,而該對象鎖被其他的線程持有時,則該線程進入阻塞狀態(tài)。當(dāng)其他線程釋放對象鎖,該線程獲得對象鎖時,該線程將重新變成可運行狀態(tài)。8.3線程的生命周期及狀態(tài)轉(zhuǎn)換(4)無限等待狀態(tài)一個線程在等待另一個線程執(zhí)行一個喚醒動作時,該線程進入無限等待狀態(tài)。線程進入這個狀態(tài)后是不能自動喚醒的,必須等待另一個線程調(diào)用notify()方法或者notifyAll()方法才能夠喚醒。8.3線程的生命周期及狀態(tài)轉(zhuǎn)換(5)計時等待狀態(tài)計時等待狀態(tài)是指線程在調(diào)用了具有指定等待時間的方法后而進入的狀態(tài),這些方法包括Thread類的sleep()方法和join()方法、Object類的wait()方法、LockSupport類的parkNanos()方法、LockSupport.parkUntil()等。當(dāng)線程調(diào)用這些方法并指定了等待時間,它就會進入計時等待狀態(tài),這一狀態(tài)將一直保持到超時或者接收到喚醒通知。8.3線程的生命周期及狀態(tài)轉(zhuǎn)換(6)被終止?fàn)顟B(tài)被終止?fàn)顟B(tài)是終止運行的線程的狀態(tài)。線程因為run()方法正常退出,或者線程拋出一個未捕獲的異常(Exception)或者錯誤(Error)時終止了run()方法而結(jié)束執(zhí)行。8.3線程的生命周期及狀態(tài)轉(zhuǎn)換在多線程程序中,線程啟動后不能永遠獨占CPU資源,CPU會在多個線程之間切換執(zhí)行。因此,線程的狀態(tài)會在可運行和阻塞之間切換。此外,通過一些操作也可以使線程在不同狀態(tài)之間轉(zhuǎn)換。線程狀態(tài)的轉(zhuǎn)換關(guān)系如下圖所示。線程同步8.48.4線程同步線程同步是指在多線程程序中,當(dāng)一個線程在對內(nèi)存中的共享數(shù)據(jù)進行操作時,其他線程無法對該共享數(shù)據(jù)進行操作,直到該線程完成操作的機制。這一機制可以確保多個線程按照特定順序正確執(zhí)行,從而保證在多線程環(huán)境下共享資源的安全訪問和正確性。當(dāng)程序中的線程不同步時,可能會導(dǎo)致線程出現(xiàn)安全問題。8.4.1線程安全問題熟悉線程安全問題,能夠簡述多線程程序會出現(xiàn)線程安全問題的原因

先定一個小目標(biāo)!8.4.1線程安全問題采用多線程技術(shù)的應(yīng)用程序可以充分利用CPU的空閑時間片,用盡可能少的時間對用戶的請求作出響應(yīng),從而提高程序整體運行效率。然而,由于線程調(diào)度具有一定的隨機性,當(dāng)多個線程同時訪問和操作同一共享資源時,很容易出現(xiàn)偶然的線程安全問題。8.4.1線程安全問題案例演示線程安全問題是指多線程環(huán)境中,多個線程同時訪問共享數(shù)據(jù),如果沒有適當(dāng)?shù)耐奖Wo機制保護共享數(shù)據(jù),導(dǎo)致程序可能出現(xiàn)不正確或不符合預(yù)期的結(jié)果。下面通過一個模擬銀行取錢的案例,演示多個線程同時訪問和操作同一共享資源產(chǎn)生的安全問題。銀行取錢基本流程如下:(1)用戶輸入取款金額。(2)銀行判斷賬戶余額是否大于取款金額。(3)如果余額大于取款金額,則取款成功,否則,取款失敗。8.4.1線程安全問題案例演示(1)定義一個賬戶類Account用于封裝賬戶信息,該類中定義賬戶編號和余額兩個成員變量,以及取錢的方法。Account.java源代碼8.4.1線程安全問題案例演示(2)定義取錢的線程類DrawThread,在該線程類的線程執(zhí)行體中執(zhí)行取錢操作,每次取100000元。DrawThread.java源代碼8.4.1線程安全問題案例演示(3)創(chuàng)建測試類ThreadDemo,在測試類的main()方法中創(chuàng)建賬戶對象,并啟動兩個線程從該賬戶中取錢。ThreadDemo.java源代碼8.4.1線程安全問題案例演示(4)運行文件ThreadDemo.java,結(jié)果如下圖所示。8.4.2線程同步方式掌握線程同步的方式,能夠使用同步代碼塊、同步方法、Lock鎖這3種方式實現(xiàn)線程同步

先定一個小目標(biāo)!8.4.2線程同步方式在多線程程序中,多個線程并發(fā)修改共享資源時,可能會導(dǎo)致出現(xiàn)線程安全問題。為避免出現(xiàn)此類問題,可以采用線程同步機制,線程同步的核心思想是保證在任何時刻,只有一個線程可以訪問共享資源。常見的線程同步方式有同步代碼塊、同步方法、Lock接口.8.4.2線程同步方式1.同步代碼塊同步代碼塊是一段被synchronized關(guān)鍵字修飾的代碼塊,可以將處理共享資源的代碼放在同步代碼塊中,以保證在同一時間只有一個線程可以執(zhí)行該代碼塊中的內(nèi)容,實現(xiàn)線程同步。使用synchronized關(guān)鍵字創(chuàng)建同步代碼塊的語法格式如下:synchronized(obj){ …此處編寫操作共享資源的代碼}obj是一個對象,作為鎖使用,只有持有該鎖的線程才可以執(zhí)行同步代碼塊中的代碼。obj可以為任意一個對象,也可以是類的字面常量。8.4.2線程同步方式1.同步代碼塊下面使用同步代碼塊解決模擬銀行取錢案例中的安全問題,修改Account.java文件中的drawMoney()方法,將取錢修改余額的代碼放入同步代碼塊中。使用同步代碼塊修改drawMoney()方法源代碼8.4.2線程同步方式1.同步代碼塊修改完成后,運行ThreadDemo.java文件,效果如下圖所示。8.4.2線程同步方式2.同步方法synchronized關(guān)鍵字還可以用于修飾方法,稱為同步方法。和同步代碼塊類似,同步方法在同一時刻只允許一個線程執(zhí)行。對于實例同步方法,默認使用當(dāng)前實例對象作為鎖對象,而對于靜態(tài)同步方法,則默認使用類對象作為鎖對象,即使用類名.class對象作為鎖對象。同步方法的synchronized關(guān)鍵字需要放在方法的返回值類型之前,具體語法格式如下:[修飾符]synchronized返回值類型方法名([參數(shù)1,...]){}8.4.2線程同步方式2.同步方法下面使用同步方法解決模擬銀行取錢案例的線程安全問題,將Account.java文件中的drawMoney()方法修改為同步方法。使用同步方法修改drawMoney()方法源代碼8.4.2線程同步方式2.同步方法8.4.2線程同步方式3.Lock接口同步代碼塊和同步方法使用的是封閉式的鎖機制,使用起來比較簡單,也能夠解決線程安全問題,但也存在一些限制,例如對于等待獲取鎖的線程,無法中斷其等待。即使等待時間很長,無法得到鎖,也只能一直等待下去,無法中途終止。這可能會導(dǎo)致系統(tǒng)資源的浪費。8.4.2線程同步方式3.Lock接口為了更清晰地表達如何加鎖和釋放鎖,從Java5開始,Java提供了一種功能強大的線程同步方式,即使用Lock接口相應(yīng)的實現(xiàn)類充當(dāng)同步鎖來實現(xiàn)同步。Lock對象可以讓某個線程在持續(xù)獲取同步鎖失敗后返回,而不再繼續(xù)等待,提供了比同步代碼塊和同步方法更廣泛的鎖操作。8.4.2線程同步方式3.Lock接口Lock是接口不能直接實例化,常用的是Lock接口的實現(xiàn)類ReentrantLock(可重入鎖)

。使用Lock對象可以顯式地加鎖、釋放鎖,加鎖的方法為lock(),釋放鎖的方法為unlock()。使用ReentrantLock的代碼格式如右所示。class類名{//定義鎖對象privateReentrantLockreentrantLock=newReentrantLock();//需要保障線程安全的方法publicvoid方法名(){reentrantLock.lock();//加鎖

try{//需要保證線程安全的代碼……}finally{reentrantLock.unlock();

//釋放鎖

}}}8.4.2線程同步方式3.Lock接口下面使用ReentrantLock解決模擬銀行取錢案例的線程安全問題,修改Account.java文件中的drawMoney()方法,使用ReentrantLock進行加鎖。使用ReentrantLock修改drawMoney()方法源代碼8.4.2線程同步方式3.Lock接口修改完成后,運行ThreadDemo.java文件,效果如下圖所示。線程的控制8.58.5線程的控制熟悉線程的控制,能夠使用線程優(yōu)先級、線程休眠、線程讓步和線程插隊實現(xiàn)線程的控制

先定一個小目標(biāo)!8.5線程的控制多線程程序中的各個線程是并發(fā)執(zhí)行的,但不是同一時刻執(zhí)行。要執(zhí)行某個線程,它必須獲得CPU的使用權(quán)。Java虛擬機根據(jù)特定的機制為程序中的每個線程分配CPU的使用權(quán)。如果需要在程序中控制線程的執(zhí)行,可以使用線程優(yōu)先級、線程休眠、線程讓步和線程插隊四種方式來實現(xiàn)。8.5線程的控制1.線程的優(yōu)先級程序運行期間,每個線程都有自己的優(yōu)先級,較高優(yōu)先級的線程具有更大概率獲得CPU的使用權(quán)。為方便操作線程的優(yōu)先級,Thread類提供了setPriority(intnewPriority)方法來設(shè)置線程的優(yōu)先級,同時還提供了getPriority()方法用于獲取線程的優(yōu)先級。8.5線程的控制1.線程的優(yōu)先級線程的優(yōu)先級使用1~10之間的整數(shù)表示,數(shù)字越大表示優(yōu)先級越高。線程的優(yōu)先級可以直接使用對應(yīng)的整數(shù)進行設(shè)置,也可以使用Thread類中的三個靜態(tài)常量進行設(shè)置,具體如下表所示。靜態(tài)常量功能描述intMAX_PRIORITY表示線程的最高優(yōu)先級,相當(dāng)于值10intMIN_PRIORITY表示線程的最低優(yōu)先級,相當(dāng)于值1intNORM_PRIORIY表示線程的普通優(yōu)先級,相當(dāng)于值58.5線程的控制1.線程的優(yōu)先級下面通過一個案例演示程序中不同優(yōu)先級線程的運行情況。Example04.java源代碼8.5線程的控制1.線程的優(yōu)先級案例的運行結(jié)果如下圖所示。8.5線程的控制1.線程的優(yōu)先級需要說明的是,Java的線程優(yōu)先級依賴于底層操作系統(tǒng)的支持。不同操作系統(tǒng)對線程優(yōu)先級的支持不同,不保證一致性和可移植性。因此,線程優(yōu)先級僅用于提高程序執(zhí)行效率,而不適用于實現(xiàn)特定功能。8.5線程的控制2.線程休眠如果想要控制線程執(zhí)行順序,使正在執(zhí)行的線程暫停,將CPU使用權(quán)讓給其他線程,可以使用Thread類的sleep()靜態(tài)方法,該方法會使當(dāng)前線程進入睡眠狀態(tài),讓出CPU使用權(quán),從而讓其他線程有機會執(zhí)行。Thread的sleep()方法接收一個表示線程要暫停的時間參數(shù),單位為毫秒。線程在暫停期間不會執(zhí)行任何操作。暫停時間過后,線程會重新進入就緒狀態(tài),等待CPU的調(diào)度再次執(zhí)行。sleep()方法可能會拋出InterruptedException異常,因此,需要在調(diào)用該方法的地方處理或聲明拋出InterruptedException異常,以便對程序可能出現(xiàn)的中斷進行適當(dāng)?shù)捻憫?yīng)。8.5線程的控制3.線程讓步線程讓步是讓當(dāng)前線程放棄對CPU的占用,使其他具有相同或更高優(yōu)先級的線程有機會繼續(xù)執(zhí)行的一種操作,可以通過Thread類的yield()方法實現(xiàn)。yield()方法和sleep()方法有些類似,都可以讓當(dāng)前正在運行的線程暫停,區(qū)別在于yield()方法不會阻塞該線程,只是將其轉(zhuǎn)換成就緒狀態(tài)。8.5線程的控制3.線程讓步下面通過一個案例演示yield()方法的使用。Example05.java源代碼8.5線程的控制3.線程讓步案例的運行結(jié)果如下圖所示。8.5線程的控制4.線程插隊在Java中,線程插隊是一種線程控制方式,它允許一個線程等待另一個線程執(zhí)行結(jié)束后,然后再繼續(xù)執(zhí)行。通過Thread類中的join()方法可以實現(xiàn)線程插隊的效果,當(dāng)一個線程對象在當(dāng)前線程中調(diào)用join()方法后,當(dāng)前線程會等待調(diào)用join()方法的線程對象所在線程完成執(zhí)行后再繼續(xù)執(zhí)行自己的任務(wù)。8.5線程的控制4.線程插隊下面通過一個案例演示使用join()方法實現(xiàn)的線程插隊效果。Example06.java源代碼8.5線程的控制4.線程插隊案例的運行結(jié)果如下圖所示。線程池8.68.6線程池熟悉線程池的使用,能夠使用ThreadPoolExecutor創(chuàng)建線程池對象,并通過線程池對象執(zhí)行線程任務(wù)

先定一個小目標(biāo)!8.6線程池線程池是一個可以復(fù)用線程的技術(shù),它是一個容納多個線程的容器。在程序啟動時,線程池會預(yù)先創(chuàng)建指定數(shù)量的線程,并讓它們處于空閑狀態(tài)。當(dāng)程序傳遞一個任務(wù)給線程池時,線程池會選擇一個空閑線程來執(zhí)行任務(wù)。任務(wù)執(zhí)行完畢后,線程會返回線程池并重新轉(zhuǎn)換為空閑狀態(tài),等待下一個任務(wù)的執(zhí)行。8.6線程池在Java中,Executor接口是線程池的頂級接口,它定義了執(zhí)行指定任務(wù)的方法,但沒有規(guī)定線程池的具體實現(xiàn)方式。創(chuàng)建線程池對象通常通過Executor的子類ExecutorService的實現(xiàn)類ThreadPoolExecutor創(chuàng)建,ThreadPoolExecutor提供了4個構(gòu)造方法創(chuàng)建線程池對象,它們的區(qū)別只是參數(shù)的數(shù)量和類型不同,其中參數(shù)最全的構(gòu)造方法的語法格式如下。publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,

longkeepAliveTime,

TimeUnitunit, BlockingQueue<Runnable>workQueue,

ThreadFactorythreadFactory,

RejectedExecutionHandlerhandler)8.6線程池ThreadPoolExecutor類的構(gòu)造方法的參數(shù)所代表的含義如下。corePoolSize:線程池的核心線程數(shù)量,不能小于0。maximumPoolSize:線程池支持的最大線程數(shù),最小數(shù)量應(yīng)大于等于核心線程數(shù)量。keepAliveTime:臨時線程的最大存活時間,不能小于0。unit:存活時間的單位,可以是秒、分、時、天。workQueue:任務(wù)隊列,不能為null。threadFactory:創(chuàng)建線程的線程工廠。handler:線程池?zé)o法處理新任務(wù)時的拒絕策略。當(dāng)線程池已達到最大線程數(shù)并且任務(wù)隊列已滿時,新提交的任務(wù)就會被拒絕執(zhí)行。8.6線程池Java根據(jù)不同場景的需求提供了幾種RejectedExecutionHandler處理策略,具體如下表所示。策略說明ThreadPoolExecutor.AbortPolicy丟棄任務(wù)并拋出RejectedExecutionException異常,ThreadPoolExecutor的默認策略ThreadPoolExecutor.DiscardPolicy丟棄任務(wù),但是不拋出異常,不推薦使用ThreadPoolEx

溫馨提示

  • 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)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論