Java編程技術(shù)與項(xiàng)目實(shí)戰(zhàn) 課件 第13章多線程_第1頁
Java編程技術(shù)與項(xiàng)目實(shí)戰(zhàn) 課件 第13章多線程_第2頁
Java編程技術(shù)與項(xiàng)目實(shí)戰(zhàn) 課件 第13章多線程_第3頁
Java編程技術(shù)與項(xiàng)目實(shí)戰(zhàn) 課件 第13章多線程_第4頁
Java編程技術(shù)與項(xiàng)目實(shí)戰(zhàn) 課件 第13章多線程_第5頁
已閱讀5頁,還剩65頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

第13章多線程01開篇引導(dǎo)故事:廚房里的廚師們,在一個(gè)繁忙的餐廳里,有一個(gè)大廚房,里面有幾位廚師正緊張而有序地工作著。這個(gè)廚房就像是一個(gè)多線程的Java程序,每位廚師都像是一個(gè)線程,他們各自負(fù)責(zé)不同的任務(wù),但共同協(xié)作以完成整個(gè)晚餐的服務(wù)。角色與任務(wù):主廚(MainChef):相當(dāng)于Java程序的主線程,他負(fù)責(zé)總體協(xié)調(diào),比如分配任務(wù)給各位廚師,以及監(jiān)控整個(gè)廚房的運(yùn)作。切菜師(VegetableChopper):一個(gè)專門的線程,負(fù)責(zé)將所有蔬菜切成需要的形狀和大小。他快速地工作,不斷從蔬菜籃中取出食材,完成切割后放在準(zhǔn)備好的盤子里。廚師A(ChefA):另一個(gè)線程,專門負(fù)責(zé)烹飪?nèi)忸?。他精通各種肉類料理,從牛排到烤雞,都能得心應(yīng)手。他時(shí)刻關(guān)注著火爐,確保每一道菜都能達(dá)到最佳口感。廚師B(ChefB):又一個(gè)線程,負(fù)責(zé)制作面食和主食。他忙著揉面團(tuán)、搟面片、包餃子,為客人準(zhǔn)備美味的碳水化合物。裝盤師(Plater):這是一個(gè)輔助線程,他的工作是在所有食材準(zhǔn)備好之后,將它們精心擺放在盤子上,裝飾成令人垂涎的佳肴。他確保每一道菜上桌時(shí)都是最美的狀態(tài)。同步與協(xié)作:共享資源:廚房中的火爐、刀具、案板等都是共享資源。廚師們需要輪流使用這些資源,避免沖突。這就像是Java中的同步代碼塊,確保同一時(shí)間只有一個(gè)線程可以訪問某個(gè)共享資源。等待與通知:當(dāng)某個(gè)廚師需要等待某種食材時(shí)(比如切菜師還沒切好胡蘿卜),他就會(huì)暫時(shí)停下手中的工作,直到食材準(zhǔn)備好。這類似于Java中的wait()和notify()機(jī)制,線程在特定條件下等待或喚醒。協(xié)作完成:雖然每位廚師都有自己的專長(zhǎng)和任務(wù),但他們必須緊密協(xié)作,才能確保晚餐服務(wù)順利進(jìn)行。這就像Java多線程程序中的各個(gè)線程,雖然各自獨(dú)立執(zhí)行,但通過共享數(shù)據(jù)和協(xié)調(diào)機(jī)制,共同完成任務(wù)。01開篇引導(dǎo)結(jié)尾:隨著夜幕降臨,餐廳逐漸熱鬧起來。廚房里的廚師們忙碌而有序地工作著,一道道美味佳肴被送到客人面前。這一切的順利進(jìn)行,都要?dú)w功于他們(線程)之間的默契協(xié)作和高效管理。通過這個(gè)小故事,我們可以更直觀地理解Java多線程的概念和重要性。在Java程序中,合理地使用多線程可以顯著提高程序的執(zhí)行效率和響應(yīng)速度,但也需要注意線程間的同步和協(xié)作問題,以避免數(shù)據(jù)錯(cuò)亂和程序崩潰的風(fēng)險(xiǎn)。01開篇引導(dǎo)知識(shí)要點(diǎn)掌握程度相關(guān)知識(shí)線程的基本概念了解程序、進(jìn)程與線程多線程的優(yōu)點(diǎn)單核與多核的概述4.并行與并發(fā)的概述線程的創(chuàng)建與啟動(dòng)掌握繼承Therad類2.實(shí)現(xiàn)Runnable接口3.匿名內(nèi)部類創(chuàng)建啟動(dòng)線程4.繼承Thread類和實(shí)現(xiàn)Runnable接口區(qū)別線程的生命周期重點(diǎn)掌握線程的生命周期多線程同步重點(diǎn)掌握資源線程安全問題2.同步機(jī)制線程的通信重點(diǎn)掌握為什么要通信2.等待喚醒機(jī)制3.線程池JDK5.0新增線程創(chuàng)建方式掌握實(shí)現(xiàn)Callable接口2.使用線程池01開篇引導(dǎo)技能要點(diǎn)掌握程度應(yīng)用方向線程同步重點(diǎn)掌握應(yīng)用開發(fā)Web開發(fā)桌面開發(fā)大數(shù)據(jù)開發(fā)線程的通信重點(diǎn)掌握應(yīng)用開發(fā)Web開發(fā)桌面開發(fā)

4.大數(shù)據(jù)開發(fā)JDK5.0新增線程的創(chuàng)建方式掌握應(yīng)用開發(fā)Web開發(fā)桌面開發(fā)

4.大數(shù)據(jù)開發(fā)多線程基本概念-程序、進(jìn)程和線程021.程序的概述

為完成特定任務(wù),用某種語言編寫的一組指令的集合。即指一段靜態(tài)的代碼,靜態(tài)對(duì)象。2.進(jìn)程的概述(1)

程序的一次執(zhí)行過程,可視為內(nèi)存中正在運(yùn)行的應(yīng)用程序?qū)嵗?,如運(yùn)行中的QQ或網(wǎng)易音樂播放器。每個(gè)進(jìn)程均擁有其獨(dú)立的內(nèi)存空間,并經(jīng)歷從創(chuàng)建、運(yùn)行到消亡的完整生命周期。(2)程序本身是靜態(tài)的代碼集合,而進(jìn)程則是其動(dòng)態(tài)執(zhí)行的體現(xiàn)。作為操作系統(tǒng)進(jìn)行資源調(diào)度和分配的最小單位(亦為系統(tǒng)運(yùn)行程序的基本單位),進(jìn)程在運(yùn)行期間會(huì)被系統(tǒng)分配獨(dú)立的內(nèi)存區(qū)域。(3)現(xiàn)代操作系統(tǒng)普遍支持多進(jìn)程環(huán)境,允許多個(gè)程序同時(shí)運(yùn)行。例如,在上課期間,我們可能同時(shí)啟動(dòng)編輯器、錄屏軟件、畫圖板以及DOS窗口等多個(gè)軟件程序,每個(gè)程序都以獨(dú)立進(jìn)程的形式在系統(tǒng)中運(yùn)行。多線程基本概念-程序、進(jìn)程和線程023.線程的概述(1)

進(jìn)程可進(jìn)一步被細(xì)化為線程,即程序內(nèi)部的一條獨(dú)立執(zhí)行路徑。每個(gè)進(jìn)程都至少包含一個(gè)線程。當(dāng)一個(gè)進(jìn)程能夠同時(shí)并行執(zhí)行多個(gè)線程時(shí),我們稱之為支持多線程處理。線程作為CPU調(diào)度和執(zhí)行的最小基本單位,其運(yùn)行效率至關(guān)重要。(2)一個(gè)進(jìn)程內(nèi)的多個(gè)線程共享相同的內(nèi)存空間,它們從同一堆內(nèi)存中分配對(duì)象,并可以相互訪問相同的變量和對(duì)象。這種共享機(jī)制極大地簡(jiǎn)化了線程間的通信過程,提升了通信效率。然而,多個(gè)線程對(duì)共享系統(tǒng)資源的操作也可能引發(fā)安全隱患,需要開發(fā)者在設(shè)計(jì)時(shí)予以充分考慮。小貼士:不同的進(jìn)程之間是不共享內(nèi)存的。進(jìn)程之間的數(shù)據(jù)交換和通信的成本很高。多線程基本概念-線程的調(diào)度02(1)分時(shí)調(diào)度所有線程按照既定規(guī)則輪流獲得CPU的使用權(quán),確保每個(gè)線程都能平均分配到相應(yīng)的CPU時(shí)間片。(2)搶占式調(diào)度在搶占式調(diào)度中,具有較高優(yōu)先級(jí)的線程擁有更大的機(jī)會(huì)優(yōu)先獲得CPU資源。若存在多個(gè)優(yōu)先級(jí)相同的線程,系統(tǒng)將隨機(jī)選擇其中一個(gè)進(jìn)行執(zhí)行,體現(xiàn)了線程調(diào)度的隨機(jī)性。值得注意的是,Java虛擬機(jī)正是采用了這種搶占式調(diào)度策略。多線程基本概念-線程的優(yōu)點(diǎn)02(1)提高應(yīng)用程序的響應(yīng)速度。這一優(yōu)化對(duì)圖形化界面尤為重要,能顯著提升用戶體驗(yàn)。(2)提升計(jì)算機(jī)系統(tǒng)CPU的利用效率。(3)優(yōu)化程序結(jié)構(gòu)。將冗長(zhǎng)且復(fù)雜的進(jìn)程合理劃分為多個(gè)線程,使其能夠獨(dú)立運(yùn)行,這樣的設(shè)計(jì)既便于理解又易于修改。多線程的基本概念-單核與多核的概述021.單核的概述在一個(gè)時(shí)間單元內(nèi),只能執(zhí)行一個(gè)線程的任務(wù)。例如,可以把CPU看成是醫(yī)院的醫(yī)生診室,在一定時(shí)間內(nèi)只能給一個(gè)病人診斷治療。所以單核CPU就是,代碼經(jīng)過前面一系列的前導(dǎo)操作(類似于醫(yī)院掛號(hào),比如有10個(gè)窗口掛號(hào)),然后到cpu處執(zhí)行時(shí)發(fā)現(xiàn),就只有一個(gè)CPU(對(duì)應(yīng)一個(gè)醫(yī)生),大家排隊(duì)執(zhí)行。2.多核的概述每個(gè)核心都具備完整的處理器功能,包括算術(shù)邏輯單元(ALU)、控制單元、寄存器等。這些核心可以并行地執(zhí)行指令,就如同多個(gè)獨(dú)立的處理器在協(xié)同工作。例如,在一個(gè)四核處理器中,就相當(dāng)于有四個(gè)獨(dú)立的處理器在同一芯片上同時(shí)運(yùn)行。多線程的基本概念-并行與并發(fā)021.并行的概述同時(shí)發(fā)生:描述兩個(gè)或多個(gè)事件在同一時(shí)間點(diǎn)共同發(fā)生的情境。此概念亦適用于技術(shù)領(lǐng)域,如在同一時(shí)刻,多條指令能夠在多個(gè)CPU上并行執(zhí)行,極大地提升了處理效率。類比于日常生活中,多個(gè)人在同一時(shí)間內(nèi)各自進(jìn)行不同的活動(dòng),體現(xiàn)了時(shí)間與資源的有效利用。2.并發(fā)的概述兩個(gè)或多個(gè)事件在同一時(shí)間段內(nèi)并行發(fā)生,即在特定的時(shí)間范圍內(nèi),多個(gè)指令在單個(gè)CPU上實(shí)現(xiàn)快速切換與交替執(zhí)行,從而在宏觀上營造出多個(gè)進(jìn)程同步運(yùn)行的效果。線程創(chuàng)建與啟動(dòng)-繼承Threrad類03Java通過繼承Thread類來創(chuàng)建并啟動(dòng)多線程,實(shí)現(xiàn)流程為:(1)定義Thread類的子類,并重寫該類的run()方法,該run()方法的方法體就代表了線程需要完成的任務(wù)。(2)創(chuàng)建Thread子類的實(shí)例,即創(chuàng)建了線程對(duì)象。(3)調(diào)用線程對(duì)象的start()方法來啟動(dòng)該線程?!纠?3-1】繼承Thread類,創(chuàng)建線程并啟動(dòng)線程,代碼如下:packagecom.chapter13.create;publicclassMyThreadDemoextendsThread{//定義指定線程名稱的構(gòu)造方法publicMyThreadDemo(Stringname){//調(diào)用父類的String參數(shù)的構(gòu)造方法,指定線程的名稱super(name);}publicvoidrun(){for(inti=0;i<10;i++){System.out.println(getName()+":正在執(zhí)行!"+i);}}}線程創(chuàng)建與啟動(dòng)-繼承Threrad類03packagecom.chapter13.create;publicclassThreadDemoTest{publicstaticvoidmain(String[]args){//創(chuàng)建自定義線程對(duì)象1MyThreadDemomyThreadDemo=newMyThreadDemo("線程1");//啟動(dòng)線程1myThreadDemo.start();//創(chuàng)建自定義線程對(duì)象2MyThreadDemomyThreadDemo02=newMyThreadDemo("線程2");//啟動(dòng)線程2myThreadDemo02.start();//在main方法種執(zhí)行for循環(huán)for(inti=1;i<=10;i++){System.out.println("main線程:"+i);}}}在上述代碼中,`MyThreadDemo`類通過繼承`Thread`類實(shí)現(xiàn)了自定義線程的創(chuàng)建。`MyThreadDemo`類中重寫了`Thread`類的`run()`方法,該方法定義了線程被調(diào)度執(zhí)行時(shí)所需完成的任務(wù)。而在`ThreadDemoTest`類中,為了啟動(dòng)這個(gè)自定義線程,必須調(diào)用其`start()`方法。線程創(chuàng)建與啟動(dòng)-繼承Threrad類03小貼士:(1)如果自己手動(dòng)調(diào)用`run()`方法,那么它就僅僅是一個(gè)普通的Java方法,并不會(huì)啟動(dòng)多線程模式。(2)`run()`方法通常由JVM(Java虛擬機(jī))在特定時(shí)刻調(diào)用,而這個(gè)調(diào)用的時(shí)機(jī)以及執(zhí)行過程的控制,則完全由操作系統(tǒng)的CPU調(diào)度來決定。(3)若想啟動(dòng)多線程,必須調(diào)用線程的`start()`方法。(4)一個(gè)線程對(duì)象只能被調(diào)用一次`start()`方法來啟動(dòng),若嘗試重復(fù)調(diào)用,則會(huì)拋出`IllegalThreadStateException`異常。(5)線程啟動(dòng)流程讓如圖13-1所示線程創(chuàng)建與啟動(dòng)-實(shí)現(xiàn)Runnable接口03Java具有單繼承的限制,當(dāng)無法直接繼承`Thread`類時(shí),我們應(yīng)如何應(yīng)對(duì)呢?幸運(yùn)的是,Java核心類庫中提供了`Runnable`接口,這一機(jī)制允許我們通過實(shí)現(xiàn)`Runnable`接口并重寫其`run()`方法來定義線程的執(zhí)行體。隨后,我們可以利用`Thread`類的對(duì)象作為代理,啟動(dòng)并執(zhí)行我們自定義的`run()`方法,從而靈活地實(shí)現(xiàn)多線程編程。在實(shí)現(xiàn)`Runnable`接口具體流程如下:(1)定義Runnable接口的實(shí)現(xiàn)類,并重新實(shí)現(xiàn)該接口的run()方法,該run()方法的具體實(shí)現(xiàn)即為該線程的線程執(zhí)行體。(2)創(chuàng)建Runnable接口實(shí)現(xiàn)類的實(shí)例,并將此實(shí)例作為Thread類的target參數(shù)來構(gòu)造Thread對(duì)象,這個(gè)Thread對(duì)象即為真正的線程實(shí)體。(3)通過調(diào)用線程對(duì)象的start()方法來啟動(dòng)線程。注意,不應(yīng)直接調(diào)用Runnable接口實(shí)現(xiàn)類的run方法,因?yàn)檫@樣做并不會(huì)啟動(dòng)一個(gè)新的線程,而是像調(diào)用普通方法一樣在當(dāng)前線程中執(zhí)行run方法的內(nèi)容。線程創(chuàng)建與啟動(dòng)-實(shí)現(xiàn)Runnable接口03【例13-2】實(shí)現(xiàn)Runnable接口,創(chuàng)建線程并啟動(dòng)線程,代碼如下:packagecom.chapter13.create;publicclassMyTreadDemo02implementsRunnable{@Overridepublicvoidrun(){for(inti=0;i<20;i++){System.out.println(Thread.currentThread().getName()+""+i);}}}線程創(chuàng)建與啟動(dòng)-實(shí)現(xiàn)Runnable接口03packagecom.chapter13.create;publicclassRunnableDemoTest{publicstaticvoidmain(String[]args){//創(chuàng)建自定義的對(duì)象,線程任務(wù)對(duì)象MyTreadDemo02myTreadDemo02=newMyTreadDemo02();//創(chuàng)建線程對(duì)象Threadthread=newThread(myTreadDemo02);//啟動(dòng)線程對(duì)象thread.start();for(inti=0;i<10;i++){System.out.println("chapter13:"+i);}}}在上述代碼中,`MyTreadDemo02`類通過實(shí)現(xiàn)`Runnable`接口,并覆寫(重寫)其`run()`方法來定義線程的執(zhí)行任務(wù)。重要的是要明確,`MyTreadDemo02`并非直接代表線程對(duì)象本身,而是作為線程任務(wù)的一個(gè)載體或?qū)ο蟠嬖凇?shí)際的線程對(duì)象需要在`RunnableDemoTest`類中通過適當(dāng)?shù)臉?gòu)造進(jìn)行創(chuàng)建,隨后通過調(diào)用`start()`方法來啟動(dòng)這一線程,從而執(zhí)行`MyTreadDemo02`類中定義的`run()`方法內(nèi)的任務(wù)。實(shí)現(xiàn)`Runnable`接口避免了單繼承的局限性,多個(gè)線程可以共享同一個(gè)接口實(shí)現(xiàn)類的對(duì)象,非常適合多個(gè)相同線程來處理同一份資源。增加程序的健壯性,實(shí)現(xiàn)解耦操作,代碼可以被多個(gè)線程共享,代碼和線程獨(dú)立。線程創(chuàng)建與啟動(dòng)-實(shí)現(xiàn)Runnable接口03小貼士:通過實(shí)現(xiàn)Runnable接口,該類便具備了多線程的特性。所有需要在分線程中執(zhí)行的代碼都應(yīng)當(dāng)被置于run方法之內(nèi)。在啟動(dòng)多線程時(shí),首先需要利用Thread類的構(gòu)造方法`Thread(Runnabletarget)`來創(chuàng)建一個(gè)Thread對(duì)象,并指定Runnable接口的實(shí)現(xiàn)類作為參數(shù)。隨后,通過調(diào)用該Thread對(duì)象的start()方法來啟動(dòng)并執(zhí)行多線程代碼。實(shí)際上,無論采用何種方式實(shí)現(xiàn)多線程——無論是繼承Thread類還是實(shí)現(xiàn)Runnable接口,最終都是依賴于Thread對(duì)象的API來控制線程的。因此,熟悉Thread類的API是進(jìn)行多線程編程不可或缺的基礎(chǔ)。需要注意的是,Runnable對(duì)象在這里僅僅是作為Thread對(duì)象的target存在,而Runnable接口實(shí)現(xiàn)類中的run()方法則充當(dāng)了線程的執(zhí)行體。盡管實(shí)際的線程對(duì)象是Thread的實(shí)例,但正是這個(gè)Thread線程負(fù)責(zé)執(zhí)行其target所指定的run()方法中的代碼。線程創(chuàng)建與啟動(dòng)-匿名內(nèi)部類創(chuàng)建啟動(dòng)線程03使用匿名內(nèi)部類對(duì)象來實(shí)現(xiàn)線程的創(chuàng)建和啟動(dòng)【例13-3】使用匿名內(nèi)部類,創(chuàng)建線程并啟動(dòng)線程,代碼如下:packagecom.chapter13.create;publicclassAnonymousDemoTest{publicstaticvoidmain(String[]args){AnonymousDemoTestanonymousDemoTest=newAnonymousDemoTest();anonymousDemoTest.testThread();anonymousDemoTest.testRunn();}線程創(chuàng)建與啟動(dòng)-匿名內(nèi)部類創(chuàng)建啟動(dòng)線程03/***使用匿名內(nèi)部創(chuàng)建線程類,調(diào)用start方法,啟動(dòng)線程*/publicvoidtestThread(){newThread(){publicvoidrun(){for(inti=0;i<10;i++){System.out.println(getName()+":正在執(zhí)行!"+i);}}}.start();}線程創(chuàng)建與啟動(dòng)-匿名內(nèi)部類創(chuàng)建啟動(dòng)線程03/***使用匿名內(nèi)部創(chuàng)建線程類,調(diào)用start方法,啟動(dòng)線程*/publicvoidtestRunn(){newThread(newRunnable(){@Overridepublicvoidrun(){for(inti=0;i<10;i++){System.out.println(Thread.currentThread().getName()+":"+i);}}}).start();}}在上述代碼中,在`testThread()`方法中,我們直接運(yùn)用了`Thread`類來實(shí)例化一個(gè)線程對(duì)象,通過重寫其`run()`方法來定義線程的執(zhí)行任務(wù),隨后調(diào)用`start()`方法來啟動(dòng)線程的執(zhí)行。而在`testRunn()`方法中,創(chuàng)建線程的方式則依賴于實(shí)現(xiàn)了`Runnable`接口的類,我們同樣需要重寫`run()`方法來指定線程的任務(wù)內(nèi)容,但啟動(dòng)線程的方式稍有不同,這里是通過`Thread`類的構(gòu)造器傳入實(shí)現(xiàn)了`Runnable`接口的實(shí)例,并調(diào)用其`start()`方法來啟動(dòng)線程。線程創(chuàng)建與啟動(dòng)-繼承Thread類與實(shí)現(xiàn)Runnable接口的區(qū)別03(1)繼承Thread類:線程代碼存放Thread子類run方法中,直接定義的就是線程類,直接啟動(dòng)線程。(2)實(shí)現(xiàn)Runnable接口:線程代碼存在接口的子類的run方法,定義的是線程任務(wù)類,需要通過Thread類創(chuàng)建線程類,再去啟動(dòng)線程。線程的生命周期04Java語言使用Thread類及其子類的對(duì)象來表示線程,在它的一個(gè)完整的生命周期中通常要經(jīng)歷如下一些狀態(tài):線程的生命周期有五種狀態(tài):新建(New)、就緒(Runnable)、運(yùn)行(Running)、阻塞(Blocked)、死亡(Dead)。CPU需要在多條線程之間切換,于是線程狀態(tài)會(huì)多次在運(yùn)行、阻塞、就緒之間切換,如圖13-2所示線程的生命周期04接下來,我們將利用表13-1將線程狀態(tài)時(shí)進(jìn)行詳細(xì)闡述。線程狀態(tài)名稱線程狀態(tài)說明NEW(新建)線程剛被創(chuàng)建,但是并未啟動(dòng)。還沒調(diào)用start方法。RUNNABLE(可運(yùn)行)這里并未對(duì)就緒與運(yùn)行狀態(tài)進(jìn)行明確區(qū)分。原因在于,針對(duì)Java對(duì)象而言,它們僅能被標(biāo)記為可運(yùn)行狀態(tài),至于何時(shí)真正執(zhí)行,則并非由JVM直接控制,而是由操作系統(tǒng)(OS)負(fù)責(zé)調(diào)度。此過程極為短暫,故而在Java對(duì)象的狀態(tài)劃分上,難以對(duì)這兩種狀態(tài)作出明確界定。Teminated(被終止)表明此線程已經(jīng)結(jié)束生命周期,終止運(yùn)行。BLOCKED(鎖阻塞)在API的說明中,這種狀態(tài)被定義為:線程正處于阻塞階段,等待獲取一個(gè)監(jiān)視器鎖(即鎖對(duì)象)。僅當(dāng)該線程成功獲取到鎖對(duì)象后,它才會(huì)獲得執(zhí)行的機(jī)會(huì)。線程的生命周期04線程狀態(tài)名稱線程狀態(tài)說明TIMED_WAITING(計(jì)時(shí)等待)在API中的介紹為:一個(gè)正在限時(shí)等待另一個(gè)線程執(zhí)行一個(gè)(喚醒)動(dòng)作的線程處于這一狀態(tài)。TERMINATED(無線等待)在API中介紹為:一個(gè)正在無限期等待另一個(gè)線程執(zhí)行一個(gè)特別的(喚醒)動(dòng)作的線程處于這一狀態(tài)。線程的生命周期04小貼士:當(dāng)從WAITING或TIMED_WAITING恢復(fù)到Runnable狀態(tài)時(shí),如果發(fā)現(xiàn)當(dāng)前線程沒有得到監(jiān)視器鎖,那么會(huì)立刻轉(zhuǎn)入BLOCKED狀態(tài),如圖13-3所示多線程線程同步-資源線程安全問題05線程安全問題主要是指多個(gè)線程同時(shí)訪問和操作同一共享資源時(shí),可能會(huì)導(dǎo)致數(shù)據(jù)的不一致性、程序的不可預(yù)測(cè)性以及錯(cuò)誤的結(jié)果?!纠?3-4】火車站票務(wù)系統(tǒng)模擬:多窗口并行售票流程為了模擬火車站的票務(wù)銷售過程,我們將構(gòu)建一個(gè)場(chǎng)景,其中本次列車的座位總數(shù)為5個(gè)(即,火車票的最大銷售數(shù)量為5張)。在此框架下,我們將模擬車站的售票窗口功能,特別是多個(gè)售票窗口同時(shí)運(yùn)行的情況,以確保高效且準(zhǔn)確的票務(wù)分配。重要提示:在模擬過程中,需嚴(yán)格遵循票務(wù)的唯一性和準(zhǔn)確性原則,避免任何形式的錯(cuò)票或重票現(xiàn)象發(fā)生,代碼如下:多線程線程同步-資源線程安全問題05packagecom.chapter13.safe;publicclassWindowDemoextendsThread{privateintticket=5;@Overridepublicvoidrun(){while(ticket>0){System.out.println(getName()+"賣出一張票,票號(hào):"+ticket);ticket--;}}}多線程線程同步-資源線程安全問題05packagecom.chapter13.safe;publicclassSafeTicketDemo{publicstaticvoidmain(String[]args){WindowDemow1=newWindowDemo();WindowDemow2=newWindowDemo();WindowDemow3=newWindowDemo();//給線程定義名稱w1.setName("窗口1");w2.setName("窗口2");w3.setName("窗口3");//啟動(dòng)線程w1.start();w2.start();w3.start();}}在上述代碼中,局部變量不可共享,若發(fā)現(xiàn)售出15張票的情況,需明確局部變量在每次方法調(diào)用時(shí)均保持獨(dú)立狀態(tài)。因此,每個(gè)線程的`run()`方法中的`ticket`變量也各自獨(dú)立,不存在數(shù)據(jù)共享問題。同樣地,不同實(shí)例對(duì)象的實(shí)例變量也各自獨(dú)立,互不共享數(shù)據(jù)。小貼士:使用靜態(tài)變量,實(shí)現(xiàn)共享數(shù)據(jù)。多線程線程同步-資源線程安全問題05【例13-5】在13-4案例基礎(chǔ)上進(jìn)行實(shí)現(xiàn)共享數(shù)據(jù),代碼如下:packagecom.chapter13.safe;publicclassWindowDemo02extendsThread{privatestaticintticket=5;publicvoidrun(){while(ticket>0){try{Thread.sleep(10);//加入這個(gè),使得問題暴露的更明顯}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(getName()+"賣出一張票,票號(hào):"+ticket);ticket--;}}}多線程線程同步-資源線程安全問題05packagecom.chapter13.safe;publicclassSafeTicketDemo03{publicstaticvoidmain(String[]args){//創(chuàng)建線程任務(wù)對(duì)象WindowDemo03tr=newWindowDemo03();//定義線程對(duì)象Threadt1=newThread(tr,"窗口一");Threadt2=newThread(tr,"窗口二");Threadt3=newThread(tr,"窗口三");//啟動(dòng)線程t1.start();t2.start();t3.start();}}在上述代碼中,發(fā)現(xiàn)賣出近5張票,但是有重復(fù)票或負(fù)數(shù)票問題,依然存在線程的安全問題,在13.4.2章節(jié)中解決線程安全問題。多線程線程同步-資源線程安全問題05【例13-6】在13-5案例的基礎(chǔ)上,實(shí)現(xiàn)同一對(duì)象實(shí)例變量的共享使用,代碼如下:packagecom.chapter13.safe;publicclassWindowDemo03implementsRunnable{privateintticket=5;publicvoidrun(){while(ticket>0){try{Thread.sleep(10);//加入這個(gè),使得問題暴露的更明顯}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"賣出一張票,票號(hào):"+ticket);ticket--;}}}多線程線程同步-資源線程安全問題05packagecom.chapter13.safe;publicclassSafeTicketDemo03{publicstaticvoidmain(String[]args){//創(chuàng)建線程任務(wù)對(duì)象WindowDemo03tr=newWindowDemo03();//定義線程對(duì)象Threadt1=newThread(tr,"窗口一");Threadt2=newThread(tr,"窗口二");Threadt3=newThread(tr,"窗口三");//啟動(dòng)線程t1.start();t2.start();t3.start();}}多線程線程同步-同步機(jī)制05要解決上述多線程并發(fā)訪問一個(gè)資源的安全性問題:也就是解決重復(fù)票與不存在票問題,Java中提供了同步機(jī)制(synchronized)來解決,如圖13-7所示。同步代碼塊:synchronized關(guān)鍵字可以用于某個(gè)區(qū)塊前面,表示只對(duì)這個(gè)區(qū)塊的資源實(shí)行互斥訪問。語法結(jié)構(gòu)如下:synchronized(同步鎖){

需要同步操作的代碼}同步方法:synchronized關(guān)鍵字直接修飾方法,表示同一時(shí)刻只有一個(gè)線程能進(jìn)入這個(gè)方法,其他線程在外面等著。多線程線程同步-同步機(jī)制05語法結(jié)構(gòu)如下:public

synchronized

void

method(){

可能會(huì)產(chǎn)生線程安全問題的代碼}同步鎖對(duì)象可以是任意類型,但是必須保證競(jìng)爭(zhēng)“同一個(gè)共享資源”的多個(gè)線程必須使用同一個(gè)“同步鎖對(duì)象”。對(duì)于同步代碼塊來說,同步鎖對(duì)象是由程序員手動(dòng)指定的(很多時(shí)候也是指定為this或類名.class),但是對(duì)于同步方法來說,同步鎖對(duì)象只能是默認(rèn)的:(1)靜態(tài)方法:當(dāng)前類的Class對(duì)象(類名.class)(2)非靜態(tài)方法:this1.同步方法(1)在靜態(tài)方法上添加同步鎖【例13-7】在基于13-6案例的情境下,實(shí)現(xiàn)數(shù)據(jù)共享機(jī)制以有效應(yīng)對(duì)并解決線程安全問題,代碼如下:packagecom.chapter13.safe;publicclassWindowDemo04extendsThread{privatestaticintticket=5;publicvoidrun(){//直接鎖這里,肯定不行,會(huì)導(dǎo)致,只有一個(gè)窗口賣票while(ticket>0){saleOneTicket();}}多線程線程同步-同步機(jī)制05publicsynchronizedstaticvoidsaleOneTicket(){//鎖對(duì)象是TicketSaleThread類的Class對(duì)象,而一個(gè)類的Class對(duì)象在內(nèi)存中肯定只有一個(gè)if(ticket>0){//不加條件,相當(dāng)于條件判斷沒有進(jìn)入鎖管控,線程安全問題就沒有解決System.out.println(Thread.currentThread().getName()+"賣出一張票,票號(hào):"+ticket);ticket--;}}}多線程線程同步-同步機(jī)制05packagecom.chapter13.safe;publicclassSafeTicketDemo04{publicstaticvoidmain(String[]args){//創(chuàng)建線程對(duì)象WindowDemo04t1=newWindowDemo04();WindowDemo04t2=newWindowDemo04();WindowDemo04t3=newWindowDemo04();//設(shè)置線程名稱t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");//啟動(dòng)線程t1.start();t2.start();t3.start();}}在上述代碼中,實(shí)現(xiàn)了數(shù)據(jù)的共享機(jī)制,通過將`synchronized`同步鎖應(yīng)用于靜態(tài)方法之上,巧妙地解決了線程安全問題,確保了數(shù)據(jù)的一致性和完整性。多線程線程同步-同步機(jī)制05(2)在非靜態(tài)方法上添加同步鎖【例13-8】在基于13-6案例的情境下,實(shí)現(xiàn)數(shù)據(jù)共享機(jī)制以有效應(yīng)對(duì)并解決線程安全問題,代碼如下:packagecom.chapter13.safe;publicclassWindowDemo05implementsRunnable{privatestaticintticket=5;publicvoidrun(){//直接鎖這里,肯定不行,會(huì)導(dǎo)致,只有一個(gè)窗口賣票while(ticket>0){saleOneTicket();}}publicsynchronizedvoidsaleOneTicket(){//鎖對(duì)象是this,這里就是TicketSaleRunnable對(duì)象,因?yàn)樯厦?個(gè)線程使用同一個(gè)TicketSaleRunnable對(duì)象,所以可以if(ticket>0){//不加條件,相當(dāng)于條件判斷沒有進(jìn)入鎖管控,線程安全問題就沒有解決System.out.println(Thread.currentThread().getName()+"賣出一張票,票號(hào):"+ticket);ticket--;}}}多線程線程同步-同步機(jī)制05packagecom.chapter13.safe;publicclassSafeTicketDemo05{publicstaticvoidmain(String[]args){//創(chuàng)建線程任務(wù)對(duì)象WindowDemo05tr=newWindowDemo05();//創(chuàng)建線程類Threadt1=newThread(tr,"窗口一");Threadt2=newThread(tr,"窗口二");Threadt3=newThread(tr,"窗口三");//啟動(dòng)線程t1.start();t2.start();t3.start();}}在上述代碼中,實(shí)現(xiàn)了數(shù)據(jù)的共享機(jī)制,通過將`synchronized`同步鎖應(yīng)用于非靜態(tài)方法之上,巧妙地解決了線程安全問題,確保了數(shù)據(jù)的一致性和完整性。多線程線程同步-同步機(jī)制052.同步代碼塊將`synchronized`同步鎖應(yīng)用在代碼塊,解決了線程安全問題,確保了數(shù)據(jù)的一致性和完整性?!纠?3-9】在基于13-6案例的情境下,實(shí)現(xiàn)數(shù)據(jù)共享機(jī)制以有效應(yīng)對(duì)并解決線程安全問題,代碼如下:packagecom.chapter13.safe;publicclassWindowDemo06{privatestaticintticket=5;publicvoidsale(){//也可以直接給這個(gè)方法加鎖,鎖對(duì)象是this,這里就是Ticket對(duì)象if(ticket>0){System.out.println(Thread.currentThread().getName()+"賣出一張票,票號(hào):"+ticket);ticket--;}else{thrownewRuntimeException("沒有票了");}}publicintgetTicket(){returnticket;}}多線程線程同步-同步機(jī)制05packagecom.chapter13.safe;publicclassSafeTicketDemo06{publicstaticvoidmain(String[]args){//2、創(chuàng)建資源對(duì)象WindowDemo06ticket=newWindowDemo06();//3、啟動(dòng)多個(gè)線程操作資源類的對(duì)象Threadt1=newThread("窗口一"){publicvoidrun(){//不能給run()直接加鎖,因?yàn)閠1,t2,t3的三個(gè)run方法分別屬于三個(gè)Thread類對(duì)象,//run方法是非靜態(tài)方法,那么鎖對(duì)象默認(rèn)選this,那么鎖對(duì)象根本不是同一個(gè)while(true){synchronized(ticket){ticket.sale();}}}};多線程線程同步-同步機(jī)制05Threadt2=newThread("窗口二"){publicvoidrun(){while(true){synchronized(ticket){ticket.sale();}}}};Threadt3=newThread(newRunnable(){publicvoidrun(){while(true){synchronized(ticket){ticket.sale();}}}},"窗口三");t1.start();t2.start();t3.start();}}在上述代碼中,實(shí)現(xiàn)了數(shù)據(jù)的共享機(jī)制,通過將`synchronized`同步鎖應(yīng)用于代碼塊之上,巧妙地解決了線程安全問題,確保了數(shù)據(jù)的一致性和完整性。多線程線程同步-同步機(jī)制05小貼士:當(dāng)窗口1的線程開始進(jìn)行操作時(shí),窗口2和窗口3的線程則需在外部等待。直到窗口1的操作完成,窗口1、窗口2以及窗口3的線程才有機(jī)會(huì)進(jìn)入代碼執(zhí)行。換言之,在某一線程對(duì)共享資源進(jìn)行修改時(shí),其他線程必須等待該修改完成并實(shí)現(xiàn)同步后,方可去競(jìng)爭(zhēng)CPU資源,完成其各自的操作。這樣的機(jī)制確保了數(shù)據(jù)的同步性,從而解決了線程不安全的問題。為確保每個(gè)線程都能正確地執(zhí)行原子操作,Java引入了線程同步機(jī)制。特別值得注意的是,在任何時(shí)候,至多只有一個(gè)線程能持有同步鎖。獲得鎖的線程將進(jìn)入代碼塊執(zhí)行,而其他線程則處于阻塞(BLOCKED)狀態(tài),等待鎖的釋放。多線程線程同步-同步機(jī)制053.同步鎖lockjava.util.concurrent.locks.Lock機(jī)制提供了比synchronized代碼和synchronize方法更廣泛的鎖定操作,同步代碼塊/同步方法具有的功能Lock都有,除此之外更強(qiáng)大,更體現(xiàn)面向?qū)ο蟆?/p>

Lock鎖也稱同步鎖,加鎖和釋放鎖方法化了。(1)publicvoidlock();加同步鎖(2)publicvoidunlock();釋放同步鎖;

【例13-10】在基于13-6案例的情境下,實(shí)現(xiàn)數(shù)據(jù)共享機(jī)制以有效應(yīng)對(duì)并解決線程安全問題,代碼如下:packagecom.chapter13.safe;importjava.util.concurrent.locks.Lock;importjava.util.concurrent.locks.ReentrantLock;publicclassTicketDemoimplementsRunnable{staticintticket=6;//定義一個(gè)變量一共賣10張票staticLockl=newReentrantLock();@Overridepublicvoidrun(){while(true){method();}}多線程線程同步-同步機(jī)制05publicstaticvoidmethod(){l.lock();if(ticket>0){try{//Thread線程對(duì)象,調(diào)用sleep對(duì)象,只要睡覺,,就會(huì)失去cpu的執(zhí)行權(quán)力,睡醒之后繼續(xù)獲的cpu的權(quán)利去執(zhí)行,參數(shù)是毫秒數(shù)Thread.sleep(1000);}catch(InterruptedExceptione){//TODOAuto-generatedcatchblocke.printStackTrace();}System.out.println(Thread.currentThread().getName()+"正在買"+ticket--);}l.unlock();}}多線程線程同步-同步機(jī)制05packagecom.chapter13.safe;publicclassTicketDemoTest{publicstaticvoidmain(String[]args){//創(chuàng)建線程任務(wù):TicketDemoticketDemo=newTicketDemo();//創(chuàng)建三個(gè)窗口Threadthread1=newThread(ticketDemo);Threadthread2=newThread(ticketDemo);Threadthread3=newThread(ticketDemo);//同時(shí)賣票thread1.start();thread2.start();thread3.start();}}在上述代碼中,實(shí)現(xiàn)了數(shù)據(jù)的共享機(jī)制,通過將`lock`同步鎖應(yīng)用于代碼塊之上,巧妙地解決了線程安全問題,確保了數(shù)據(jù)的一致性和完整性。線程的通信-等待喚醒機(jī)制05"wait"狀態(tài)指的是線程進(jìn)入非活動(dòng)狀態(tài),不再參與CPU調(diào)度,被加入至等待集合(waitset)中。在此狀態(tài)下,線程既不會(huì)消耗CPU資源,也不會(huì)參與鎖的競(jìng)爭(zhēng),其狀態(tài)標(biāo)記為WAITING或TIMED_WAITING。線程需要等待其他線程執(zhí)行特定的操作——“通知(notify)”或等待設(shè)定的時(shí)間到期,之后才會(huì)從等待集合中被釋放,并重新進(jìn)入調(diào)度隊(duì)列(readyqueue)中,準(zhǔn)備執(zhí)行。"notify"操作則是針對(duì)某一對(duì)象的等待集合中的一個(gè)線程進(jìn)行釋放,允許其繼續(xù)執(zhí)行。而"notifyAll"操作則是針對(duì)同一對(duì)象的等待集合中的所有線程進(jìn)行釋放,使它們都能繼續(xù)執(zhí)行。被喚醒的線程即便被通知,也可能無法立即恢復(fù)執(zhí)行,原因在于其原本中斷之處位于同步塊內(nèi),且當(dāng)前已不再持有所需的鎖。因此,它必須重新嘗試獲取鎖(這很可能涉及與其他線程的競(jìng)爭(zhēng)),一旦成功,方能在調(diào)用wait方法后的斷點(diǎn)處繼續(xù)執(zhí)行。小貼士:如果能成功獲取鎖,線程將從WAITING狀態(tài)轉(zhuǎn)變?yōu)镽UNNABLE(可運(yùn)行)狀態(tài);否則,線程將保持WAITING狀態(tài)并轉(zhuǎn)變?yōu)锽LOCKED(等待鎖)狀態(tài)。1.wait()方法和notify()方法的使用【例13-11】使用兩個(gè)線程交替打印數(shù)字1至100,線程1與線程2輪流輸出序列中的每個(gè)數(shù)字,代碼如下:線程的通信-等待喚醒機(jī)制05packagemunication;publicclassCommunicationDemoimplementsRunnable{inti=1;publicvoidrun(){while(true){synchronized(this){notify();if(i<=6){System.out.println(Thread.currentThread().getName()+":"+i++);}elsebreak;try{wait();}catch(InterruptedExceptione){e.printStackTrace();}}}}}線程的通信-等待喚醒機(jī)制05packagemunication;importernal.generic.DCMPG;publicclassCommunicationDemoTest{publicstaticvoidmain(String[]args){CommunicationDemocommunicationDemo=newCommunicationDemo();Threadthread=newThread(communicationDemo);Threadthread1=newThread(communicationDemo);thread.setName("線程1");thread1.setName("線程2");thread.start();thread1.start();}}在上述代碼中,使用`wait()`方法實(shí)現(xiàn)線程間的等待機(jī)制,確保線程間的有序執(zhí)行。首先線程1執(zhí)行打印操作,隨后線程2進(jìn)入等待狀態(tài);接著線程1暫停,線程2被`notify()`方法喚醒并執(zhí)行打印操作;最后線程2暫停,等待線程1的后續(xù)操作或再次被喚醒。這種機(jī)制有效地控制了線程的執(zhí)行順序。線程的通信-等待喚醒機(jī)制05小貼士:(1)wait方法和notify方法必須由同一個(gè)鎖對(duì)象來調(diào)用。原因如下:對(duì)應(yīng)的鎖對(duì)象能夠通過notify操作喚醒那些因調(diào)用該鎖對(duì)象的wait方法而進(jìn)入等待狀態(tài)的線程。(2)wait方法和notify方法是Object類的成員方法。這是因?yàn)椋涸贘ava中,鎖對(duì)象可以是任意對(duì)象,而所有Java對(duì)象都直接或間接地繼承了Object類,因此它們都可以使用這兩個(gè)方法。wait方法和notify方法必須在同步代碼塊或同步方法中調(diào)用。這是因?yàn)椋哼@兩個(gè)方法的調(diào)用必須依賴于鎖對(duì)象,如果不在同步代碼塊或同步方法中,就沒有明確的鎖對(duì)象來調(diào)用它們,這時(shí)會(huì)拋出java.lang.IllegalMonitorStateException異常。線程的通信-等待喚醒機(jī)制052.生產(chǎn)者與消費(fèi)者生產(chǎn)者與消費(fèi)者問題實(shí)質(zhì)上蘊(yùn)含了兩大核心議題:(1)線程安全挑戰(zhàn):鑒于生產(chǎn)者與消費(fèi)者共享數(shù)據(jù)緩沖區(qū)的特性,這自然引發(fā)了安全性的考量。不過,這一問題可通過實(shí)施同步機(jī)制來有效化解。(2)線程協(xié)作難題:①為解決此難題,關(guān)鍵在于確保生產(chǎn)者線程在緩沖區(qū)滿載時(shí)能夠自動(dòng)進(jìn)入等待(wait)狀態(tài),即進(jìn)入阻塞模式,直至消費(fèi)者線程消耗了緩沖區(qū)中的數(shù)據(jù)并發(fā)出通知(notify),使等待的線程恢復(fù)就緒狀態(tài),繼續(xù)向緩沖區(qū)添加數(shù)據(jù)。②同理,消費(fèi)者線程在緩沖區(qū)為空時(shí)也應(yīng)進(jìn)入等待狀態(tài),暫停執(zhí)行直至生產(chǎn)者向緩沖區(qū)添加新數(shù)據(jù)并發(fā)出通知,隨后消費(fèi)者線程方可恢復(fù)并繼續(xù)處理數(shù)據(jù)。這種精巧的通信機(jī)制正是解決此類協(xié)作問題的關(guān)鍵所在。線程的通信-等待喚醒機(jī)制05【例13-12】生產(chǎn)者(Producer)將產(chǎn)品交付給店員(Clerk),消費(fèi)者(Customer)則從店員處領(lǐng)取所需產(chǎn)品。店員一次僅能管理固定數(shù)量的產(chǎn)品(例如:5件)。若生產(chǎn)者欲生產(chǎn)超出此數(shù)量的產(chǎn)品,店員會(huì)指示其暫停生產(chǎn),待店內(nèi)空間充足時(shí)再行通知恢復(fù)生產(chǎn)。相反,若店內(nèi)產(chǎn)品售罄,店員會(huì)告知消費(fèi)者稍作等待,一旦有新產(chǎn)品入庫便立即通知其前來領(lǐng)取,代碼如下:packagemunication;publicclassClerk{Stringpi;Stringxian;booleanflag=false;}packagemunication;publicclassProducerimplementsRunnable{privateClerkbz;publicProducer(Clerkbz){this.bz=bz;}線程的通信-等待喚醒機(jī)制05@Overridepublicvoidrun(){while(true){synchronized(bz){//對(duì)包子鋪進(jìn)行判斷if(bz.flag==true){//調(diào)用wait方法,進(jìn)行等待try{bz.wait();}catch(InterruptedExceptione){e.printStackTrace();}}/*包子鋪線程開始生產(chǎn)包子*/bz.pi="薄皮";bz.xian="大餡";//生產(chǎn)的是xx皮xx餡System.out.println("包子鋪正在生產(chǎn)包子,生產(chǎn)的是:"+bz.pi+bz.xian+"請(qǐng)稍微等待幾秒");線程的通信-等待喚醒機(jī)制05//生產(chǎn)包子花費(fèi)了3秒try{Thread.sleep(3000);}catch(InterruptedExceptione){e.printStackTrace();}//生產(chǎn)包子完畢之后,修改包子的狀態(tài)true,bz.flag=true;bz.notify();System.out.println("包子鋪已經(jīng)生產(chǎn)好了美味的"+bz.pi+bz.xian+"的包子");}}}}線程的通信-等待喚醒機(jī)制05//生產(chǎn)包子花費(fèi)了3秒try{Thread.sleep(3000);}catch(InterruptedExceptione){e.printStackTrace();}//生產(chǎn)包子完畢之后,修改包子的狀態(tài)true,bz.flag=true;bz.notify();System.out.println("包子鋪已經(jīng)生產(chǎn)好了美味的"+bz.pi+bz.xian+"的包子");}}}}線程的通信-等待喚醒機(jī)制05packagemunication;publicclassConsumerimplementsRunnable{privateClerkbz;publicConsumer(Clerkbz){this.bz=bz;}@Overridepublicvoidrun(){while(true){synchronized(bz){//判斷是否有包子if(bz.flag==false){try{bz.wait();}catch(InterruptedExceptione){e.printStackTrace();}}線程的通信-等待喚醒機(jī)制05//吃貨線程開始吃包子System.out.println("吃貨正在吃包子,吃的是"+bz.pi+bz.xian+"包子");//吃完包子修改包子的狀態(tài)bz.flag=false;//吃貨線程喚醒包子鋪線程,做包子bz.notify();//喚醒包子上等待的包子鋪線程System.out.println("吃貨已經(jīng)吃完了包子,包子鋪趕緊生產(chǎn)包子把");System.out.println("============================");}}}}線程的通信-等待喚醒機(jī)制05packagemunication;publicclassConsumerProducerTest{publicstaticvoidmain(String[]args){Clerkbz=newClerk();ProducerbaoziPu=newProducer(bz);Consumerch=newConsumer(bz);newThread(baoziPu).start();newThread(ch).start();}}在上述代碼中,Clerk類代表店員,維護(hù)一個(gè)存儲(chǔ)產(chǎn)品的隊(duì)列和信號(hào)量來控制生產(chǎn)者和消費(fèi)者的操作。Producer類代表生產(chǎn)者,不斷生產(chǎn)產(chǎn)品并交給店員。Consumer類代表消費(fèi)者,從店員處領(lǐng)取產(chǎn)品。通過信號(hào)量的控制,確保了店員的庫存容量不超過固定數(shù)量,并且在庫存為空時(shí)消費(fèi)者會(huì)等待,在庫存滿時(shí)生產(chǎn)者會(huì)等待。線程的通信-等待喚醒機(jī)制054.鎖的操作(1)釋放鎖的操作①在任何線程嘗試進(jìn)入一個(gè)同步代碼塊或同步方法之前,它必須首先獲得相應(yīng)的同步監(jiān)視器的鎖定。當(dāng)前線程完成同步方法或同步代碼塊的執(zhí)行。②當(dāng)前線程在同步代碼塊或同步方法中遇到break或return語句,導(dǎo)致代碼塊或方法的執(zhí)行被終止。③當(dāng)前線程在同步代碼塊或同步方法中遇到未處理的Error或Exception,導(dǎo)致線程異常終止。④當(dāng)前線程在同步代碼塊或同步方法中調(diào)用了鎖對(duì)象的wait()方法,這將導(dǎo)致當(dāng)前線程被掛起,并同時(shí)釋放鎖。請(qǐng)注意,以上情況均不會(huì)改變?cè)牡幕疽馑己秃诵挠^點(diǎn)。(2)不會(huì)釋放鎖的操作①線程執(zhí)行同步代碼塊或同步方法時(shí),程序調(diào)用Thread.sleep()、Thread.yield()方法暫停當(dāng)前線程的執(zhí)行。②線程執(zhí)行同步代碼塊時(shí),其他線程調(diào)用了該線程的suspend()方法將該該線程掛起,該線程不會(huì)釋放鎖(同步監(jiān)視器)。③應(yīng)盡量避免使用suspend()和resume()這樣的過時(shí)來控制線程。線程池-為什么使用線程池06當(dāng)系統(tǒng)中存在大量并發(fā)線程,且每個(gè)線程僅執(zhí)行短暫任務(wù)后即結(jié)束,頻繁地創(chuàng)建和銷毀線程會(huì)顯著降低系統(tǒng)效率。這是因?yàn)榫€程的創(chuàng)建和銷毀過程本身消耗時(shí)間。那么,是否存在一種機(jī)制,使得線程能夠被重復(fù)利用呢?即線程在完成一個(gè)任務(wù)后,并不立即銷毀,而是繼續(xù)執(zhí)行其他任務(wù)。解決方案是預(yù)先創(chuàng)建一組線程,并將它們存儲(chǔ)在所謂的線程池中。當(dāng)需要執(zhí)行任務(wù)時(shí),可以直接從線程池中獲取線程,任務(wù)完成后,線程被歸還到池中,從而避免了頻繁的創(chuàng)建和銷毀過程,實(shí)現(xiàn)了線程的復(fù)用。這與我們生活中的公共交通工具模式類似,如圖13-8所示線程池-線程池的優(yōu)點(diǎn)06(1)提高響應(yīng)速度(減少了創(chuàng)建新線程的時(shí)間)(2)降低資源消耗(重復(fù)利用線程池中線程,不需要每次都創(chuàng)建)(3)便于線程管理①corePoolSize:核心池的大?、趍aximumPoolSize:最大線程數(shù)③keepAliveTime:線程沒有任務(wù)時(shí)最多保持多長(zhǎng)時(shí)間后會(huì)終止④…線程池-線程池相關(guān)API06JDK5.0之前,我們必須手動(dòng)自定義線程池。從JDK5.0開始,Java內(nèi)置線程池相關(guān)的API。在java.util.concurrent包下提供了線程池相關(guān)API:ExecutorService和Executors。(1)ExecutorService:真正的線程池接口。常見子類ThreadPoolExecutor①voidexecute(Runnablecommand):執(zhí)行任務(wù)/命令,沒有返回值,一般用來執(zhí)行Runnable②<T>Future<T>submit(Callable<T>task):執(zhí)行任務(wù),有返回值,一般又來執(zhí)行Callable③voidshutdown():

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(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)論