




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
JaVa程序設計任務式教程01任務6-1模擬紅綠燈系統(tǒng)02任務6-2模擬環(huán)保檢測系統(tǒng)04任務6-4模擬在線購物網(wǎng)站目錄CONTNETS單元六
多線程03任務6-3模擬銀行取款系統(tǒng)單元目標能夠使用三種方式創(chuàng)建線程。能夠設置線程的優(yōu)先級、休眠、讓步、插隊、同步機制和鎖機制。能夠使用線程與線程控制分別實現(xiàn)模擬紅綠燈系統(tǒng)與環(huán)保檢測系統(tǒng)。能夠靈活運用線程的單例模式與線程池。能夠使用線程同步與線程池分別實現(xiàn)模擬銀行取款系統(tǒng)與在線購物網(wǎng)站。通過對多線程編程的學習,培養(yǎng)學生分析和解決并發(fā)問題的能力。通過模擬現(xiàn)實生活中的案例,提升學生的編程能力和邏輯思維能力。知識目標能力目標素養(yǎng)目標學習目標理解線程的定義與線程的生命周期。掌握創(chuàng)建線程的三種方式。掌握線程的優(yōu)先級、休眠、讓步、插隊的設置。掌握線程的同步機制、鎖機制、雙重檢查枷鎖機制的設置。掌握餓漢式與懶漢式的設置。掌握線程池的原理與創(chuàng)建方式。01任務6-1模擬紅綠燈系統(tǒng)線程概述繼承Thread類創(chuàng)建線程實現(xiàn)Runnable接口創(chuàng)建線程實現(xiàn)Callable接口創(chuàng)建線程線程的生命周期任務6-1模擬紅綠燈系統(tǒng)-線程的創(chuàng)建與啟動線程概述在多任務操作系統(tǒng)中,每個運行的程序都是一個進程,用來執(zhí)行不同的任務,而在一個進程中還可以有多個執(zhí)行單元同時運行,來同時完成一個或多個程序任務,這些執(zhí)行單元可以看作程序執(zhí)行的多條線索,稱為線程。操作系統(tǒng)的每一個進程中都至少存在一個線程,當一個Java程序啟動時,就會產(chǎn)生一個進程,該進程中會默認創(chuàng)建一個線程,在這個線程上會運行main()方法中的代碼。任務6-1模擬紅綠燈系統(tǒng)-線程的創(chuàng)建與啟動線程概述在多任務操作系統(tǒng)中,每個運行的程序都是一個進程,用來執(zhí)行不同的任務,而在一個進程中還可以有多個執(zhí)行單元同時運行,來同時完成一個或多個程序任務,這些執(zhí)行單元可以看作程序執(zhí)行的多條線索,稱為線程。操作系統(tǒng)的每一個進程中都至少存在一個線程,當一個Java程序啟動時,就會產(chǎn)生一個進程,該進程中會默認創(chuàng)建一個線程,在這個線程上會運行main()方法中的代碼多線程程序在運行時,每個線程之間都是獨立的,它們可以并發(fā)執(zhí)行。程序中的單線程和多線程的主要區(qū)別可以通過一張圖示來簡單說明任務6-1模擬紅綠燈系統(tǒng)-線程的創(chuàng)建與啟動繼承Thread類創(chuàng)建線程Thread類是java.lang包下的一個線程類,用來實現(xiàn)Java多線程。使用繼承Thread類的方式創(chuàng)建與啟動線程的主要步驟如下:(1)創(chuàng)建一個Thread線程類的子類(子線程),并重寫Thread類的run()方法;(2)創(chuàng)建Thread子類的實例對象,并調(diào)用start()方法啟動線程。任務6-1模擬紅綠燈系統(tǒng)-線程的創(chuàng)建與啟動繼承Thread類創(chuàng)建線程例6-1所示,通過一個例子演示如何通過繼承Thread類的方式創(chuàng)建多線程,首先在Eclipse中創(chuàng)建一個名為Chapter06的程序,在該程序的src文件夾中創(chuàng)建名為com.example.thread的包,在該包中創(chuàng)建ExampleThread類,在該類中實現(xiàn)創(chuàng)建與啟動線程,詳見ExampleThread.java1 packagecom.example.thread;2 classMyThreadextendsThread{3 //創(chuàng)建子線程類的有參構(gòu)造方法,參數(shù)為線程名稱4 publicMyThread(Stringname){5 super(name);6 }7 //重寫Thread類的run()方法8 publicvoidrun(){9 inti=0;10 while(i++<5){11 System.out.println(Thread.currentThread().getName()12 +"的run()方法在運行");13 }14 }15 }16 publicclassExampleThread{17 publicstaticvoidmain(String[]args){18 //2.創(chuàng)建MyThread1實例對象19 MyThreadthread1=newMyThread("thread1");20 //調(diào)用start()方法啟動線程21 thread1.start();22 //創(chuàng)建并啟動另一個線程myThread223 MyThreadthread2=newMyThread("thread2");24 thread2.start();25 }26 }任務6-1模擬紅綠燈系統(tǒng)-線程的創(chuàng)建與啟動繼承Thread類創(chuàng)建線程上述代碼中,第2~15行代碼定義了一個MyThread類繼承Thread類,并重寫了run()方法,其中currentThread()方法是Thread類的靜態(tài)方法,用于獲取當前線程對象,getName()方法用于獲取線程名稱。第19、23行代碼創(chuàng)建了兩個線程實例,并指定線程名稱為thread1和thread2。第21、24行代碼調(diào)用start()方法啟動線程。運行結(jié)果如圖任務6-1模擬紅綠燈系統(tǒng)-線程的創(chuàng)建與啟動實現(xiàn)Runnable接口創(chuàng)建線程Java中一個類只能繼承一個父類,而通過繼承Thread類來創(chuàng)建線程就意味著將線程的功能與類的繼承耦合在一起,導致無法再繼承其他類。而實現(xiàn)Runnable接口的方式,可以避免Java單繼承所帶來的局限性,使類能夠繼承其他類,提高代碼的靈活性。使用實現(xiàn)Runnable接口的方式創(chuàng)建并啟動線程的具體步驟如下。(1)定義一個類實現(xiàn)Runnable接口,并重寫該接口中的run()方法。(2)創(chuàng)建實現(xiàn)Runnable接口的類的對象,將該對象作為參數(shù)傳入到創(chuàng)建Thread對象的構(gòu)造方法中,這樣就實現(xiàn)了將程序的任務邏輯與線程對象分離的效果。(3)調(diào)用Thread對象的start()方法,啟動線程。任務6-1模擬紅綠燈系統(tǒng)-線程的創(chuàng)建與啟動例6-2所示,通過一個例子演示使用Runnable接口的方式來創(chuàng)建和啟動線程,首先在Chapter06程序的com.example.thread包中創(chuàng)建ExampleRunnable類,在該類中實現(xiàn)創(chuàng)建與啟動線程,詳見ExampleRunnable.java1 packagecom.example.thread;2 classMyRunnableimplementsRunnable{3 publicvoidrun(){4 for(inti=1;i<=5;i++){5 System.out.println("線程"+Thread.currentThread().getId()+":"+i); 6 try{7 Thread.sleep(1000);//讓線程休眠一段時間,模擬耗時操作
8 }catch(InterruptedExceptione){9 e.printStackTrace();10 }11 }12 }13 }14 publicclassExampleRunnable{15 publicstaticvoidmain(String[]args){16 MyRunnablemyRunnable=newMyRunnable();17 Threadthread1=newThread(myRunnable);18 Threadthread2=newThread(myRunnable);19 thread1.start();20 thread2.start();21 }22 }實現(xiàn)Runnable接口創(chuàng)建線程任務6-1模擬紅綠燈系統(tǒng)-線程的創(chuàng)建與啟動上述代碼中,第5~6行代碼中的“Thread.currentThread().getId()”表示返回當前線程的唯一標識符,即線程ID。第18~19行代碼創(chuàng)建了兩個Thread對象,并將MyRunnable對象作為參數(shù)傳遞給它們,然后調(diào)用start()方法啟動線程thread1與thread2。運行結(jié)果如圖實現(xiàn)Runnable接口創(chuàng)建線程任務6-1模擬紅綠燈系統(tǒng)-線程的創(chuàng)建與啟動實現(xiàn)Callable接口創(chuàng)建線程JDK5.0開始,Java提供了Callable接口,允許開發(fā)人員在實現(xiàn)Callable接口的類中重寫call()方法可以作為線程的執(zhí)行體。與run()方法不同,call()方法有返回值且可以拋出異常。使用實現(xiàn)Callable接口的方式創(chuàng)建并啟動線程的具體步驟如下。(1)定義Callable接口實現(xiàn)類,指定返回值類型,并重寫call()方法。(2)創(chuàng)建Callable實現(xiàn)類的實例,使用FutureTask類包裝Callable對象,F(xiàn)utureTask對象封裝了Callable對象的call()方法的返回值。(3)使用FutureTask對象作為Thread對象的target創(chuàng)建并啟動新線程。(4)調(diào)用FutureTask對象的get()方法獲得子線程執(zhí)行結(jié)束后的返回值。Callable接口不是Runnable接口的子接口,因此不能直接作為Thread的目標(target)運行,因為call()方法是在實現(xiàn)Callable接口的類中定義的,JDK5.0還提供了一個Future接口來代表call()方法的返回值。FutureTask類是Future接口的實現(xiàn)類,同時,它也實現(xiàn)了Runnable接口,因此可以作為Thread類的target任務6-1模擬紅綠燈系統(tǒng)-線程的創(chuàng)建與啟動實現(xiàn)Callable接口創(chuàng)建線程Future接口的方法方法聲明功能描述booleancancel(booleanb)試圖取消對此任務的執(zhí)行Vget()如有必要,等待計算完成,然后獲取其結(jié)果Vget(longtimeout,TimeUnitunit)如有必要,最多等待為使計算完成所給定的時間之后,獲取其結(jié)果(如果結(jié)果可用)booleanisCancelled()如果在任務正常完成前將其取消,則返回truebooleanisDone()如果任務已完成,則返回true任務6-1模擬紅綠燈系統(tǒng)-線程的創(chuàng)建與啟動實現(xiàn)Callable接口創(chuàng)建線程如例6-3所示,通過一個例子演示使用實現(xiàn)Callable接口的方式創(chuàng)建和啟動線程,并獲取1~100的整數(shù)之和。首先在Chapter06程序的com.example.thread包中創(chuàng)建ExampleCallable類,在該類中通過FutureTask的get()方法獲得新線程的執(zhí)行結(jié)果,并將1~100的整數(shù)之和的結(jié)果輸出到控制臺中,詳見ExampleCallable.java1 packagecom.example.thread;2 importjava.util.concurrent.*;3 publicclassExampleCallable{4 publicstaticvoidmain(String[]args)throwsException{5 MyCallablemyCallable=newMyCallable();6 FutureTask<Integer>futureTask=newFutureTask<>(myCallable);7 Threadthread=newThread(futureTask);8 thread.start();9 intresult=futureTask.get();10 System.out.println("計算結(jié)果:"+result);11 }12 }13 classMyCallableimplementsCallable<Integer>{14 @Override15 publicIntegercall()throwsException{16 intsum=0;17 for(inti=1;i<=100;i++){18 sum+=i;19 }20 returnsum;21 }22 } 任務6-1模擬紅綠燈系統(tǒng)-線程的創(chuàng)建與啟動實現(xiàn)Callable接口創(chuàng)建線程上述代碼中,第13~22行代碼創(chuàng)建了MyCallable類實現(xiàn)Callable接口,并在call()方法中使用for循環(huán)實現(xiàn)了1~100的整數(shù)求和邏輯。第6行代碼使用FutureTask封裝MyCallable對象,第7~8行代碼通過Thread創(chuàng)建一個新線程并啟動該線程。第9行代碼調(diào)用FutureTask的get()方法獲得子線程的執(zhí)行結(jié)果。運行結(jié)果如圖任務6-1模擬紅綠燈系統(tǒng)-線程的生命周期
線程有新建(New)、就緒(Runnable)、運行(Running)、阻塞(Blocked)和死亡(Terminated)五種狀態(tài),線程從新建到死亡的過程稱為線程的生命周期,線程的生命周期及狀態(tài)轉(zhuǎn)換,如圖任務6-1模擬紅綠燈系統(tǒng)-任務實現(xiàn)1.在Chapter06程序中創(chuàng)建com.example.task包,用于存放本單元中每個任務的代碼文件。2.在com.example.task包中創(chuàng)建TrafficLight類,用于實現(xiàn)模擬紅綠燈系統(tǒng)。3.使用3個常量定義紅燈、黃燈和綠燈亮起后的時間。4.定義一個changeLight()方法用于切換紅綠燈的3種狀態(tài)。5.在main()方法中創(chuàng)建TrafficLight類的實例,并啟動一個線程,在線程中調(diào)用changeLight()方法開啟紅綠燈的狀態(tài)切換。代碼參考教材中的TrafficLight.java任務6-1模擬紅綠燈系統(tǒng)-任務實現(xiàn)模擬紅綠燈系統(tǒng)的運行結(jié)果如圖02任務6-2模擬環(huán)保檢測系統(tǒng)線程優(yōu)先級線程休眠線程讓步線程插隊后臺線程任務6-2模擬環(huán)保檢測系統(tǒng)-線程控制操作線程優(yōu)先級所有處于就緒狀態(tài)的線程根據(jù)優(yōu)先級存放在可運行池中,優(yōu)先級低的線程運行機會較少,優(yōu)先級高的線程運行機會更多。Thread類的setPriority(intnewPriority)方法和getPriority()方法分別用于設置優(yōu)先級和讀取優(yōu)先級。優(yōu)先級用整數(shù)表示,取值范圍1~10,除了直接用數(shù)字表示線程的優(yōu)先級,還可以用Thread類中提供的三個靜態(tài)常量來表示線程的優(yōu)先級,如表常量聲明功能描述staticintMAX_PRIORITY取值為10,表示最高優(yōu)先級staticintNORM_PRIORITY取值為5,表示默認優(yōu)先級staticintMIN_PRIORITY取值為1,表示最低優(yōu)先級任務6-2模擬環(huán)保檢測系統(tǒng)-線程控制操作線程優(yōu)先級例6-4所示,通過一個例子演示線程優(yōu)先級的使用。首先在Chapter06程序的com.example.thread包中創(chuàng)建ExamplePriority類,然后在該類中演示線程優(yōu)先級的使用,詳見ExamplePriority.java1 packagecom.example.thread;2 publicclassExamplePriority{3 publicstaticvoidmain(String[]args){4 //創(chuàng)建SubThread實例
5 SubThreadst1=newSubThread("優(yōu)先級低的線程"); 6 SubThreadst2=newSubThread("優(yōu)先級高的線程");7 //設置優(yōu)先級
8 st1.setPriority(Thread.MIN_PRIORITY); 9 st2.setPriority(Thread.MAX_PRIORITY);10 //開啟線程
11 st1.start(); 12 st2.start();13 }14 }15 classSubThreadextendsThread{16 publicSubThread(Stringname){17 super(name);18 }19 publicvoidrun(){//重寫run()方法
20 for(inti=0;i<10;i++){21 if(i%2!=0){22 System.out.println(Thread.23 currentThread().getName()+":"+i);………任務6-2模擬環(huán)保檢測系統(tǒng)-線程控制操作線程優(yōu)先級上述代碼中,第15~27行代碼定義了SubThread類繼承Thread類,在該類中重寫了run()方法,在run()方法內(nèi)循環(huán)打印小于10的奇數(shù)。第5~6行代碼創(chuàng)建了兩個SubThread類的實例,并指定線程的名分別是“優(yōu)先級低的線程”與“優(yōu)先級高的線程”。第8~9行代碼調(diào)用setPriority()方法設置線程的優(yōu)先級,第11~12行代碼調(diào)用start()方法啟動線程。運行結(jié)果如圖任務6-2模擬環(huán)保檢測系統(tǒng)-線程控制操作線程休眠前面講解了線程的優(yōu)先級,可以發(fā)現(xiàn)將需要后執(zhí)行的線程設置為低優(yōu)先級,也有一定幾率先執(zhí)行該線程,可以用Thread類的靜態(tài)方法sleep()來解決這一問題,sleep()方法有兩種重載形式,具體示例如下:sleep(longmillis)sleep(longmillis,intnanos)上述示例中是sleep()方法的兩種重載形式,前者參數(shù)millis是指定線程休眠的毫秒數(shù),后者參數(shù)millis和nanos分別用于指定線程休眠的毫秒數(shù)和毫微秒數(shù)。正在執(zhí)行的線程調(diào)用sleep()方法可以進入阻塞狀態(tài),也叫線程休眠,在休眠時間內(nèi),即使系統(tǒng)中沒有其他可執(zhí)行的線程,該線程也不會執(zhí)行,當休眠時間結(jié)束后該線程才可以執(zhí)行任務6-2模擬環(huán)保檢測系統(tǒng)-線程控制操作線程休眠如例6-5所示,通過一個例子演示線程的休眠。首先在Chapter06程序的com.example.thread包中創(chuàng)建AlarmClock類,然后在該類中使用線程休眠模擬一個定時鬧鐘的場景,詳見AlarmClock.java1 packagecom.example.thread;2 importjava.text.SimpleDateFormat;3 importjava.util.Date;4 publicclassAlarmClock{5 //設定鬧鐘在5秒后響起
6 privatestaticfinallongWAKE_UP_TIME_MILLIS=5000;7 publicstaticvoidmain(String[]args){8 System.out.println("設置鬧鐘"+(WAKE_UP_TIME_MILLIS/1000)9 +"秒后響起");10 System.out.println("當前時間:"+newSimpleDateFormat("hh:mm:ss").11 format(newDate()));12 //模擬鬧鐘的線程
13 ThreadalarmClockThread=newThread(()->{14 try{15 //設置線程休眠5秒
16 Thread.sleep(WAKE_UP_TIME_MILLIS);17 //調(diào)用鬧鐘響起方法
18 ringAlarm();19 }catch(InterruptedExceptione){20 e.printStackTrace();21 }22 });23 alarmClockThread.start();24 }25 //模擬鬧鐘響起
26 privatestaticvoidringAlarm(){27 System.out.println("起床啦,太陽已經(jīng)升起來啦!");28 System.out.println("當前時間:"+newSimpleDateFormat("hh:mm:ss").29 format(newDate()));30 }31 } 任務6-2模擬環(huán)保檢測系統(tǒng)-線程控制操作線程休眠運行結(jié)果如圖任務6-2模擬環(huán)保檢測系統(tǒng)-線程控制操作線程讓步Thread類還提供一個yield()方法,它與sleep()方法類似,它也可以讓當前正在執(zhí)行的線程暫停,但是yield()方法不會使線程阻塞,只是將線程轉(zhuǎn)換為就緒狀態(tài),也就是讓當前線程暫停一下,線程調(diào)度器重新調(diào)度一次,有可能還會將暫停的程序調(diào)度出來繼續(xù)執(zhí)行,這也稱為線程讓步。任務6-2模擬環(huán)保檢測系統(tǒng)-線程控制操作線程讓步如例6-6所示,通過一個例子演示線程讓步。首先在Chapter06程序的com.example.thread包中創(chuàng)建ExampleYield類,然后在該類中演示線程讓步,詳見ExampleYield.java1 packagecom.example.thread;2 publicclassExampleYield{3 publicstaticvoidmain(String[]args){4 YieldThreadyt=newYieldThread();//創(chuàng)建YieldThread實例
5 newThread(yt,"線程1").start();//創(chuàng)建并開啟線程
6 newThread(yt,"線程2").start();7 }8 }9 classYieldThreadimplementsRunnable{10 publicvoidrun(){//重寫run()方法
11 for(inti=1;i<=6;i++){12 System.out.println(Thread.currentThread().getName()13 +":"+i);14 if(i%3==0){15 Thread.yield();16 }17 }18 }19 } 任務6-2模擬環(huán)保檢測系統(tǒng)-線程控制操作線程讓步上述代碼中,第9~19行代碼定義了YieldThread類實現(xiàn)Runnable接口,并重寫了run()方法,在run()方法內(nèi)循環(huán)打印變量i,當變量i能被3整除時,調(diào)用yield()方法讓線程讓步。第4~6行代碼首先創(chuàng)建YieldThread類的實例,然后創(chuàng)建并開啟兩個線程。運行結(jié)果如圖任務6-2模擬環(huán)保檢測系統(tǒng)-線程控制操作線程插隊Thread類提供了一個join()方法,當某個線程在執(zhí)行中調(diào)用其他線程的join()方法時,此線程將被阻塞,直到被join()方法加入的線程執(zhí)行完為止,也稱為線程插隊。任務6-2模擬環(huán)保檢測系統(tǒng)-線程控制操作線程插隊如例6-7所示,首先在Chapter06程序的com.example.thread包中創(chuàng)建ExampleJoin類,然后在該類中使用線程插隊模擬烤面包機工作的過程,將每個烤面包周期看作是一個線程的運行,而sleep()方法用來模擬烤面包所需的時間,join()方法用來等待烤面包機線程結(jié)束。詳見ExampleJoin.java1packagecom.example.thread;2publicclassExampleJoin{3 publicstaticvoidmain(String[]args){4 //創(chuàng)建并啟動一個烤面包機線程
5 ThreadtoasterThread=newThread(newToasterRunnable());6 toasterThread.start();7 //主線程等待烤面包機線程完成
8 try{9 toasterThread.join();10 }catch(InterruptedExceptione){11 e.printStackTrace();12 }13 System.out.println("所有面包片都已經(jīng)烤好!");14 }15 //創(chuàng)建一個實現(xiàn)了Runnable接口的烤面包機類ToasterRunnable16 staticclassToasterRunnableimplementsRunnable{17 @Override18 publicvoidrun(){19 //假設我們要烤4片面包
20 for(inti=0;i<4;i++){21 //模擬放入面包片
22 System.out.println("放入第"+(i+1)+"片面包");23 try{24 Thread.sleep(3000);//模擬烤面包所需的時間為3秒
25 }catch(InterruptedExceptione){26 e.printStackTrace();27 return;28 }29 //模擬取出烤好的面包片
30 System.out.println("第"+(i+1)+"片面包已經(jīng)烤好");31 }32 }33 }34} 任務6-2模擬環(huán)保檢測系統(tǒng)-線程控制操作線程插隊上述代碼中,第16~33行代碼定義了ToasterRunnable類實現(xiàn)Runnable接口,在該類中實現(xiàn)了run()方法,在run()方法中通過for循環(huán)模擬烤4片面包,在for循環(huán)中調(diào)用sleep()方法模擬烤面包片需要的時間。第5~6行代碼首先創(chuàng)建一個線程對象toasterThread,然后開啟該線程。第9行代碼調(diào)用join()方法等待toasterThread線程執(zhí)行結(jié)束,toasterThread線程執(zhí)行結(jié)束后,主線程才繼續(xù)執(zhí)行,程序調(diào)用println()方法輸出“所有面包片都已經(jīng)烤好!”信息。運行結(jié)果如圖任務6-2模擬環(huán)保檢測系統(tǒng)-線程控制操作后臺線程線程中還有一種后臺線程,它是為其他線程提供服務的,又稱為“守護線程”或“精靈線程”,JVM的垃圾回收線程就是典型的后臺線程。如果所有的前臺線程都死亡,后臺線程會自動死亡。當整個虛擬機中只剩下后臺線程,程序就沒有繼續(xù)運行的必要了,所以虛擬機也就退出了。若想要將一個線程設置為后臺線程,可以調(diào)用Thread類的setDaemon(booleanon)方法,將參數(shù)指定為true即可,Thread類還提供了一個isDaemon()方法,用于判斷一個線程是否是后臺線程。任務6-2模擬環(huán)保檢測系統(tǒng)-線程控制操作后臺線程如例6-8所示,通過一個例子演示后臺線程。首先在Chapter06程序的com.example.thread包中創(chuàng)建ExampleBack類,然后在該類中使用后臺線程模擬一個智能溫度控制系統(tǒng),這個系統(tǒng)會在后臺運行,不斷地檢查當前室內(nèi)溫度,并根據(jù)預設規(guī)則調(diào)整室內(nèi)溫度。詳見ExampleBack.java......45publicstaticvoidmain(String[]args){46 //創(chuàng)建并啟動后臺線程
47 ThreadmonitorThread=newTemperatureMonitorThread();48 monitorThread.setDaemon(true);//設置為守護線程
49 monitorThread.start();50 //模擬用戶輸入來改變房間溫度
51@SuppressWarnings("resource")52 Scannerscanner=newScanner(System.in);53 while(true){54 System.out.println("請輸入房間溫度(0-100,或輸入'exit'退出):");55 Stringinput=scanner.nextLine();56 if("exit".equalsIgnoreCase(input)){57 System.exit(0);//退出程序
58 }59 try{60 setRoomTemperature(Integer.parseInt(input));61 }catch(NumberFormatExceptione){62 System.out.println("輸入無效,請輸入一個整數(shù)。");63 }64 }65 }66}任務6-2模擬環(huán)保檢測系統(tǒng)-線程控制操作后臺線程上述代碼中,第10~35行代碼定義了TemperatureMonitorThread類繼承Thread類,并重寫run()方法,在該方法中調(diào)用while循環(huán)與sleep()方法實現(xiàn)每隔5秒檢查一次房間內(nèi)的溫度。第37~44行代碼定義了setRoomTemperature()方法,用于輸出更新后的房間溫度。第47~49行代碼首先創(chuàng)建TemperatureMonitorThread類的對象monitorThread,然后調(diào)用setDaemon()方法將monitorThread線程設置為后臺線程,最后調(diào)用start()方法開啟該線程。后臺線程的運行結(jié)果如圖1.在com.example.task包中創(chuàng)建EnProSystem類,用于實現(xiàn)模擬環(huán)保檢測系統(tǒng)。2.創(chuàng)建一個PollutionSource類繼承Thread類,在該類的run()方法中模擬檢測環(huán)境的污染級別。3.在EnProSystem類中創(chuàng)建三個不同級別的污染源,每個污染源都是一個線程。4.設置三個污染源線程的優(yōu)先級并啟動線程。5.調(diào)用join()方法等待所有線程執(zhí)行完成后,輸出“所有污染源檢測完成”。代碼參考教材中的EnProSystem.java任務6-2模擬環(huán)保檢測系統(tǒng)-線程控制操作模擬環(huán)保檢測系統(tǒng)的運行結(jié)果如圖任務6-2模擬環(huán)保檢測系統(tǒng)-線程控制操作03任務6-3模擬銀行取款系統(tǒng)線程安全線程同步機制鎖機制任務6-3模擬銀行取款系統(tǒng)-線程同步線程安全線程安全是指當多個線程并發(fā)訪問共享資源時,如果對這些資源的訪問是線程安全的,那么就可以保證在任何時候,只有一個線程能夠修改這些資源,從而避免了數(shù)據(jù)不一致或臟讀等問題。任務6-3模擬銀行取款系統(tǒng)-線程同步線程安全如例6-9所示,演示窗口賣票的經(jīng)典問題,假如總票數(shù)為6,有4個窗口在賣票。首先在Chapter06程序的com.example.thread包中創(chuàng)建ExampleTicket類,然后在該類中創(chuàng)建并開啟4個線程模擬4個賣票的窗口,詳見ExampleTicket.java……4 Ticketticket=newTicket();5 Threadt1=newThread(ticket,"火車站窗口1");6 Threadt2=newThread(ticket,"火車站窗口2");7 Threadt3=newThread(ticket,"火車站窗口3");8 Threadt4=newThread(ticket,"火車站窗口4");9 t1.start();10 t2.start();11 t3.start();12 t4.start();13 }14}15classTicketimplementsRunnable{16 privateintticket=6;17 publicvoidrun(){18 for(inti=0;i<6;i++){19 if(ticket>0){20 try{21 Thread.sleep(100);22 }catch(InterruptedExceptione){23 e.printStackTrace();24 }25 System.out.println(Thread.currentThread().getName()+"賣出第"+ticket+"張票,還剩"+--ticket+"張票");……任務6-3模擬銀行取款系統(tǒng)-線程同步線程安全上述代碼中,第21行代碼調(diào)用sleep()方法讓當前線程睡眠100毫秒,當前線程處于休眠狀態(tài)時,讓其他線程去搶資源,可以讓線程安全問題更明顯。在實際項目中,sleep()方法經(jīng)常用來模擬網(wǎng)絡延遲。運行結(jié)果如圖任務6-3模擬銀行取款系統(tǒng)-線程同步線程同步機制同步代碼塊同步代碼塊是指在代碼塊前加上synchronized關(guān)鍵字,用于解決多線程并發(fā)執(zhí)行時可能產(chǎn)生的資源沖突問題。同步代碼塊表示同一時間只能有一個線程進入到該代碼塊中執(zhí)行,從而確保共享資源的唯一性和準確性。同步代碼塊的語法格式如下。synchronized(this){ //操作共享數(shù)據(jù)的代碼}上述語法格式中,synchronized關(guān)鍵字后括號里的this就是同步鎖,當線程執(zhí)行同步代碼塊時,首先會檢查同步鎖的標志位,默認情況下標志位為1,線程會執(zhí)行同步代碼塊,同時將標志位改為0,當?shù)诙€線程執(zhí)行同步代碼塊前,檢查到標志位為0,第二個線程會進入阻塞狀態(tài),直到前一個線程執(zhí)行完同步代碼塊內(nèi)的操作,標志位重新改為1,第二個線程才有可能進入同步代碼塊。任務6-3模擬銀行取款系統(tǒng)-線程同步線程同步機制同步代碼塊通過修改例6-11中的代碼來演示使用同步代碼塊解決線程安全問題的方式,修改后的主要代碼如下所示1......2classTicketimplementsRunnable{3privateintticket=6;4publicvoidrun(){5for(inti=0;i<6;i++){6synchronized(this){7if(ticket>0){8try{9Thread.sleep(100);10}catch(InterruptedExceptione){11e.printStackTrace();12}13System.out.println(14Thread.currentThread().getName()+"賣出第"15+ticket+"張票,還剩"+--ticket+"張票");16}17}18}19}20}任務6-3模擬銀行取款系統(tǒng)-線程同步線程同步機制同步代碼塊上述代碼中,在run()方法的循環(huán)中,將變量ticket的操作都放在同步代碼塊中,當使用同步代碼塊時必須指定一個需要同步的對象作為同步鎖,一般使用當前對象(this)即可。運行結(jié)果如圖任務6-3模擬銀行取款系統(tǒng)-線程同步線程同步機制同步方法除了用同步代碼塊解決線程安全問題之外,Java還提供了同步方法,即使用synchronized關(guān)鍵字修飾方法,該方法就是同步方法。已知Java的每個對象都可以作為一個內(nèi)置鎖,當用synchronized關(guān)鍵字修飾方法時,內(nèi)置鎖會保護整個方法,在調(diào)用該方法前,需要獲得內(nèi)置鎖,否則當前線程就處于阻塞狀態(tài)任務6-3模擬銀行取款系統(tǒng)-線程同步線程同步機制同步方法通過修改例6-11的代碼來演示使用同步方法解決線程安全問題的方式,修改后的代碼如下所示1......2classTicketimplementsRunnable{3privateintticket=6;4publicsynchronizedvoidrun(){5for(inti=0;i<6;i++){6if(ticket>0){7try{8Thread.sleep(100);9}catch(InterruptedExceptione){10e.printStackTrace();11}12System.out.println(13Thread.currentThread().getName()+"賣出第"14+ticket+"張票,還剩"+--ticket+"張票");15}16}17}18}任務6-3模擬銀行取款系統(tǒng)-線程同步線程同步機制同步方法上述代碼中,將run()方法用synchronized關(guān)鍵字修飾,此時run()方法就為同步方法。運行結(jié)果如圖任務6-3模擬銀行取款系統(tǒng)-線程同步鎖機制由于synchronized有一個缺點,那就是一個線程必須等待前一個線程執(zhí)行完之后才能去執(zhí)行,如果前一個線程有耗時操作,則后一個線程一直在等待的狀態(tài)中,這就導致程序不靈活且效率低下,所以在Java6中加入了Lock來解決這個問題。Lock接口是Java并發(fā)編程中提供的一個更靈活的鎖機制,它作為synchronized的一個替代,提供了更廣泛的鎖定操作。Lock接口由ReentrantLock類實現(xiàn),同步代碼塊和同步方法具有的功能Lock都有,除此之外Lock更強大,更體現(xiàn)面向?qū)ο蟆H蝿?-3模擬銀行取款系統(tǒng)-線程同步鎖機制例6-10所示,演示鎖機制解決線程安全的問題,首先創(chuàng)建ExampleLockTicket類,然后在該類中使用鎖機制解決4個窗口賣票的線程安全問題,詳見ExampleLockTicket.java4publicclassExampleLockTicket{5 privateinttickets=6;//假設總共有6張票
6 //使用ReentrantLock作為鎖
7 privatefinalLocklock=newReentrantLock();8 //售票方法
9 publicvoidsellTicket(){10 lock.lock();//獲取鎖
11 try{12 if(tickets>0){13 System.out.println(Thread.currentThread().getName()14+"賣出第"+tickets+"張票,還剩"+--tickets+"張票");15 }else{16 System.out.println(Thread.currentThread().getName()17+"票已售完");18 }19 }finally{20 lock.unlock();//釋放鎖
21 }22 }23 publicstaticvoidmain(String[]args){24 ExampleLockTicketseller=newExampleLockTicket();25 //創(chuàng)建并啟動4個線程來模擬4個售票窗口
26 for(inti=0;i<4;i++){27 newThread(()->{28 while(true){//循環(huán)賣票直到票售完
29 seller.sellTicket();30 if(seller.tickets<=0){31 break;//票售完則退出循環(huán)
32 }33 try{34 Thread.sleep(100);//模擬售票間隔
35 }catch(InterruptedExceptione){36 e.printStackTrace();37 }38 }39 },"火車站窗口"+(i+1)).start();40 }41 }42}
任務6-3模擬銀行取款系統(tǒng)-線程同步鎖機制鎖機制解決線程安全的運行結(jié)果如圖1.在com.example.task包中創(chuàng)建BankWithdrawal類,在該類中創(chuàng)建1個銀行賬戶和3個客戶線程,并啟動3個客戶線程。2.創(chuàng)建一個Customer類繼承Thread類,表示一個取款客戶,每個客戶都有一個銀行賬戶和要取款的金額,在run()方法中調(diào)用withdraw()方法進行取款操作。3.創(chuàng)建一個BankAccount類,表示一個銀行賬戶,在該類中定義1個變量balance,表示銀行賬戶余額;定義withdraw()方法用于執(zhí)行取款操作,該方法用synchronized修飾,以確保任何時候都只有一個客戶在執(zhí)行取款操作。代碼參考教材中的BankWithdrawal.java任務6-3模擬銀行取款系統(tǒng)-線程控制操作模擬銀行取款系統(tǒng)的運行結(jié)果如圖任務6-3模擬銀行取款系統(tǒng)-線程控制操作04任務6-4模擬在線購物網(wǎng)站單例模式概述餓漢式懶漢式雙重檢查加鎖機制任務6-4模擬在線購物網(wǎng)站-單例模式單例模式概述單例模式是面向?qū)ο缶幊讨凶畛R姷脑O計模式之一。其核心思想是確保一個類僅有一個實例,并提供一個全局訪問點來訪問這個實例。單例模式的主要優(yōu)點在于它可以避免由于多個實例導致的資源消耗和數(shù)據(jù)不一致等問題。任務6-4模擬在線購物網(wǎng)站-單例模式單例模式概述1.單例模式的實現(xiàn)方式單例模式的實現(xiàn)方式有以下5種:(1) 懶漢式:在第一次調(diào)用getInstance()方法(該方法在后續(xù)會講)時創(chuàng)建實例。這種方式需要處理同步問題來確保線程安全。(2) 餓漢式:在類加載時創(chuàng)建實例,這種方式是線程安全的,但如果實例一直未使用,可能會浪費一些內(nèi)存。(3) 雙重檢查鎖定:結(jié)合了懶漢式和餓漢式的優(yōu)點,既保證了線程安全,又避免了在類加載時創(chuàng)建實例。(4) 靜態(tài)內(nèi)部類:利用了類加載機制保證初始化實例時只有一個線程,一般推薦這種方式實現(xiàn)單例模式,因為它既簡潔又高效。(5) 枚舉:在Java中,枚舉類型是單例的,并且絕對線程安全。任務6-4模擬在線購物網(wǎng)站-單例模式單例模式概述2.單例模式的使用場景單例模式的使用場景有以下3種:(1) 想要確保某個類只有一個實例,并且提供一個全局訪問點。(2) 頻繁實例化一個對象并銷毀它會造成大量性能開銷。(3) 對象需要被共享,并且在整個系統(tǒng)中只需要一個實例。任務6-4模擬在線購物網(wǎng)站-單例模式單例模式概述3.單例模式使用時的注意事項單例模式使用時有以下3個注意事項:(1) 不要在單例類中提供公開的setXX()方法,因為這可能會破壞單例的約束。(2) 如果需要序列化和反序列化,請確保在反序列化過程中不會創(chuàng)建新的實例。(3) 在多線程環(huán)境下,確保單例模式的實現(xiàn)是線程安全的。任務6-4模擬在線購物網(wǎng)站-單例模式餓漢模式餓漢式單例模式是設計模式中的一種,主要用于確保一個類在任何情況下都有且僅有一個實例,并提供一個全局訪問點。其特點在于當類加載時就已經(jīng)完成了類的實例化,避免了線程同步的問題。餓漢式單例模式的示例代碼如下1publicclassSingleton{2//當類加載時就完成了類的實例化,保證了線程安全
3privatestaticfinalSingletonINSTANCE=newSingleton();4//構(gòu)造函數(shù)私有,防止其他類創(chuàng)建該類的實例
5privateSingleton(){}6/**7 *公有靜態(tài)方法,提供全局訪問點
8 */9publicstaticSingletongetInstance(){10returnINSTANCE;11}12}任務6-4模擬在線購物網(wǎng)站-單例模式餓漢模式例6-11所示,通過智能門鎖系統(tǒng)演示餓漢式單例模式。首先創(chuàng)建HungryDoorLock類,然后在該類中使用餓漢式單例模式可以在家庭中的任何地方獲取到同一個門鎖系統(tǒng)實例,并進行開啟門鎖、關(guān)閉門鎖和記錄門鎖訪問日志等操作。詳見HungryDoorLock.java1packagecom.example.thread;2publicclassHungryDoorLock{3 //餓漢式單例,在類加載時創(chuàng)建實例
4 privatestaticfinalHungryDoorLockINSTANCE=newHungryDoorLock();5 privateHungryDoorLock(){6 //初始化門鎖系統(tǒng),比如加載門鎖配置、連接門鎖硬件等
7 System.out.println("初始化門鎖系統(tǒng)");8 }9 /**10 *公有靜態(tài)方法,用于獲取門鎖系統(tǒng)實例
11 */12 publicstaticHungryDoorLockgetInstance(){13 returnINSTANCE;14 }15 /**16 *控制門鎖開啟的方法
17 */18 publicvoidunlock(){19 System.out.println("開啟門鎖");20 }21 /**22 *控制門鎖關(guān)閉的方法
23 */24 publicvoidlock(){25 System.out.println("關(guān)閉門鎖");26 }27 /**28 *記錄門鎖訪問日志的方法
29 */30 publicvoidlogAccess(Stringname,Stringaction){31 System.out.println("用戶"+name+action+"門鎖");32 }33 publicstaticvoidmain(String[]args){34 //獲取智能門鎖系統(tǒng)實例
35 HungryDoorLockdoorLock=HungryDoorLock.getInstance();36 doorLock.unlock();//模擬用戶開啟門鎖
37 doorLock.lock();//模擬用戶關(guān)閉門鎖
38 //記錄用戶訪問日志
39 doorLock.logAccess("小明","開啟");40 }41}任務6-4模擬在線購物網(wǎng)站-單例模式餓漢模式餓漢式單例模式的運行結(jié)果如圖任務6-4模擬在線購物網(wǎng)站-單例模式懶漢模式懶漢式單例模式是一種延遲初始化的單例模式,即在第一次使用該類時才創(chuàng)建實例,相對餓漢式顯得“不急迫”,所以被叫做懶漢式。懶漢式單例模式的示例代碼如下1publicclassSingleton{2 //私有靜態(tài)成員變量,用于存儲唯一的實例
3 privatestaticSingletoninstance;4 //私有構(gòu)造方法,防止外部實例化
5 privateSingleton(){6 }7 /**8 *公有靜態(tài)方法,提供全局訪問點
9 */10 publicstaticSingletongetInstance(){11 //檢查實例是否已經(jīng)被創(chuàng)建
12 if(instance==null){13 //如果沒有被創(chuàng)建,則創(chuàng)建實例
14 instance=newSingleton();15 }16 returninstance;//返回實例
17 }18}任務6-4模擬在線購物網(wǎng)站-單例模式雙重檢查加鎖機制餓漢式和懶漢式的單例模式都有多線程不安全的缺點,雙重檢查加鎖機制解決了這兩者的缺點。雙重檢查加鎖機制也被稱為雙重檢查鎖定或雙重檢查鎖定模式,它是一種用于多線程編程的同步機制,旨在減少鎖競爭的開銷,提高程序的性能,這種機制通常用于延遲初始化單例模式。雙重檢查加鎖機制的工作流程如下所示。1.并不是每次進入相關(guān)方法(如getInstance()方法)都需要同步,而是首先在不進行同步的情況下進入相關(guān)方法。2.在相關(guān)方法內(nèi)部,首先檢查所需的實例是否存在(第一重檢查)。3.如果實例不存在,則進入同步塊。在同步塊內(nèi)部,再次檢查實例是否存在(第二重檢查)。4.如果實例在同步塊內(nèi)部仍然不存在,則在同步的情況下創(chuàng)建一個實例任務6-4模擬在線購物網(wǎng)站-單例模式雙重檢查加鎖機制雙重檢查加鎖機制的示例代碼如下1publicclassSingleton{2//使用volatile關(guān)鍵字防止指令重排
3privatevolatilestaticSingletoninstance;4privateSingleton(){}5publicstaticSingletongetInstance(){6//第一次檢查實例是否已經(jīng)被創(chuàng)建
7if(instance==null){8//同步塊,防止多個線程同時進入
9synchronized(Singleton.class){10//第二次檢查實例是否已經(jīng)被其他線程創(chuàng)建
11if(instance==null){12//如果沒有被創(chuàng)建,則在此處創(chuàng)建實例
13instance=newSingleton();14}15}16}17returninstance;//返回單例實例
18}19}任務6-4模擬在線購物網(wǎng)站-單例模式雙重檢查加鎖機制雙重檢查加鎖機制的示例代碼如下1publicclassSingleton{2//使用volatile關(guān)鍵字防止指令重排
3privatevolatilestaticSingletoninstance;4privateSingleton(){}5publicstaticSingletongetInstance(){6//第一次檢查實例是否已經(jīng)被創(chuàng)建
7if(instance==null){8//同步塊,防止多個線程同時進入
9synchronized(Singleton.class){10//第二次檢查實例是否已經(jīng)被其他線程創(chuàng)建
11if(instance==null){12//如果沒有被創(chuàng)建,則在此處創(chuàng)建實例
13instance=newSingleton();14}15}16}17returninstance;//返回單例實例
18}19}任務6-4模擬在線購物網(wǎng)
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 商業(yè)合作信譽證明(6篇)
- 工作狀態(tài)離職證明書正式離職情況詳細記錄(8篇)
- 家庭資產(chǎn)配置與理財規(guī)劃建議表
- 青島宏巨面試題及答案
- rabbitmq面試題及答案大廠java面試
- 從課本中看世界對知識的探索與感悟15篇
- 網(wǎng)頁設計試題及答案
- 血氣分析試題及答案
- 網(wǎng)絡java面試題及答案
- 魚類迷宮測試題及答案
- 互聯(lián)網(wǎng)醫(yī)療可行性研究報告
- 四川省成都市本年度(2025)小學一年級數(shù)學部編版小升初模擬((上下)學期)試卷及答案
- 無線電管理一體化平臺互聯(lián)互通規(guī)范
- 《企業(yè)內(nèi)部控制問題研究-以康美藥業(yè)公司為例》9000字(論文)
- 一年級下冊語文課件統(tǒng)編版-11 浪花【新課標版】
- 區(qū)域國別研究的跨學科性
- 園藝論文開題報告范文
- 兒科學知到智慧樹章節(jié)測試課后答案2024年秋山東第一醫(yī)科大學
- 林業(yè)工程開工申請
- 2025年甘肅省中考語文作文預測題及范文
- 高等教育信息化建設方案
評論
0/150
提交評論