




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
第八章多線程程序設(shè)計8.1多線程編程基礎(chǔ)8.2線程間的數(shù)據(jù)共享8.3多線程同步8.4線程通信8.5線程的生命周期8.6線程的優(yōu)先級8.1多線程編程基礎(chǔ)線程基本概念Thread類Runnable接口Thread類常用方法8.1.1線程的概念多重編程:一臺計算機一次運行多個應(yīng)用程序(如同時運行Excel和Word)在現(xiàn)代計算系統(tǒng)中,通常有多個要執(zhí)行的并發(fā)應(yīng)用程序進程。在計算機系統(tǒng)中,有多個等待執(zhí)行的進程,即它們正在等待將CPU分配給它們并開始執(zhí)行的時間,這些過程也稱為作業(yè)?,F(xiàn)在,主內(nèi)存太小,無法容納所有這些進程或作業(yè)。因此,這些過程最初保留在稱為作業(yè)池的區(qū)域中。該作業(yè)池由所有等待分配主內(nèi)存和CPU的進程組成。CPU從所有這些等待的作業(yè)中選擇一個作業(yè),將其從作業(yè)池移至主內(nèi)存并開始執(zhí)行。處理器執(zhí)行一項作業(yè),直到它被某個外部因素中斷或執(zhí)行I/O任務(wù)為止。多處理:一臺計算機一次使用多個CPU系統(tǒng)在單個計算機系統(tǒng)中支持一個以上處理器的能力。現(xiàn)在,由于有多個處理器可用,可以一次執(zhí)行多個進程。多重處理和多重編程的區(qū)別,多重處理基本上是在多個處理器上同時執(zhí)行多個進程,而多重編程是將多個程序保留在主存儲器中,并僅使用單個CPU并行執(zhí)行。多重處理是通過并行處理發(fā)生的,而多重編程是通過從一個過程切換到另一個過程而發(fā)生的(現(xiàn)象稱為上下文切換)。8.1.1線程的概念多任務(wù)處理:任務(wù)共享一個公共資源(如1個CPU)多任務(wù)處理是指一次執(zhí)行多個任務(wù)(例如,進程,程序,線程等)。在現(xiàn)代操作系統(tǒng)中,能夠播放MP3音樂,在MicrosoftWord中編輯文檔,同時瀏覽GoogleChrome,這是通過多任務(wù)處理來完成的。多任務(wù)處理是多程序設(shè)計的邏輯擴展。多任務(wù)與多編程不同的主要方式是,多編程僅基于上下文切換的概念進行工作,而多任務(wù)則基于時間共享以及上下文切換的概念。多線程:多任務(wù)的擴展線程是CPU利用率的基本單位。多線程是一種執(zhí)行模型,它允許單個進程具有在該進程的“上下文”中并發(fā)運行的多個代碼段(即線程)。例如:媒體播放器,其中一個線程用于打開媒體播放器,一個線程用于播放特定歌曲,另一個線程用于向播放列表添加新歌曲。多線程是一種過程,一次可以管理一個以上的用戶,并可以管理同一用戶的多個請求,而不必擁有多個程序副本。進程介紹進程(Process):是正在運行程序的實例,是操作系統(tǒng)資源分配的基本單元。每個進程都有自己獨立的內(nèi)存空間和系統(tǒng)資源。簡單理解:程序的執(zhí)行過程8.1.1線程的概念1.獨立性:每一個進程都有自己的空間,在沒有經(jīng)過進程本身
允許的情況下,一個進程不可以直接訪問其它的的進程空間動態(tài)性:進程是動態(tài)產(chǎn)生,動態(tài)消亡的并發(fā)性:任何進程都可以同其它進程一起并發(fā)執(zhí)行并行和并發(fā)并行:在同一時刻,有多個指令在多個CPU上【同時】執(zhí)行并發(fā):在同一時刻,有多個指令在單個CPU上【交替】執(zhí)行8.1.1線程的概念多進程同時執(zhí)行8.1.1線程的概念線程介紹線程(Thread):是進程的執(zhí)行單元,是cpu調(diào)度的最小單位。一個進程可以包含多個線程,他們共享進程的內(nèi)存空間和系統(tǒng)資源獨立性:每一個進程都有自己的空間,在沒有經(jīng)過進程本身允許的情況下,一個進程不可以直接訪問其它的的進程空間8.1.1線程的概念多線程的意義隨著處理器上的核心數(shù)量越來越多,現(xiàn)在大多數(shù)計算機都比以往更加擅長并行計算一個線程,在一個時刻,只能運行在一個處理器核心上8.1.1線程的概念4核8線程8核16線程16核32線程32核64線程2核4線程8.1.1線程的概念4核8線程8核16線程16核32線程32核64線程2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念2核4線程8.1.1線程的概念多線程的意義提高執(zhí)行效率同時處理多個任務(wù)8.1.1線程的概念8.1.1線程的概念多任務(wù)環(huán)境中的應(yīng)用程序進程中的線程進程是可以并行執(zhí)行的代碼片段,線程之間可以共享數(shù)據(jù)8.1.1線程的概念進程一個獨立程序的每一次運行稱為一個進程,例如用字處理軟件編輯文稿時,同時打開播放程序聽音樂,這兩個獨立的程序在同時運行,稱為兩個進程。設(shè)置一個進程要占用相當(dāng)一部分處理器時間和內(nèi)存資源大多數(shù)操作系統(tǒng)不允許進程訪問其他進程的內(nèi)存空間,進程間的通信很不方便,編程模型比較復(fù)雜。8.1.1線程的概念線程一個程序中多段代碼同時并發(fā)執(zhí)行,稱為多線程通過多線程,一個進程表面上看同時可以執(zhí)行一個以上的任務(wù)——并發(fā)創(chuàng)建線程比創(chuàng)建進程開銷要小得多,線程之間的協(xié)作和數(shù)據(jù)交換也比較容易Java是第一個支持內(nèi)置線程操作的主流編程語言多數(shù)程序設(shè)計語言支持多線程要借助于操作系統(tǒng)“原語(primitives)”8.1.1線程的概念進程vs線程多個進程的內(nèi)部數(shù)據(jù)和狀態(tài)都是完全獨立的,多線程可以共享進程所擁有的內(nèi)存空間中的同一區(qū)域以及系統(tǒng)資源,有可能相互影響。線程本身運行所需的環(huán)境比較簡單,通常只包括寄存器及堆棧,切換速度比進程快得多。進程是操作系統(tǒng)分配內(nèi)存及其他系統(tǒng)資源的基本單元,線程是操作系統(tǒng)分配CPU的基本單元。在開發(fā)中應(yīng)用多線程技術(shù)的主要目的是“最大限度地利用CPU資源”。比如當(dāng)某一線程的處理不需要占用CPU時,就會讓給其他線程使用。多任務(wù)處理是指一次執(zhí)行多個任務(wù)(例如,進程,程序,線程等)。8.1.1線程的概念Java線程的6種狀態(tài)新建狀態(tài)運行狀態(tài)等待狀態(tài)阻塞狀態(tài)終止?fàn)顟B(tài)計時等待狀態(tài)8.1.2Thread類Thread類在Java程序中創(chuàng)建多線程的方法之一是繼承Thread類封裝了Java程序中一個線程對象需要擁有的屬性和方法從Thread類派生一個子類,并創(chuàng)建這個子類的對象,就可以產(chǎn)生一個新的線程。這個子類應(yīng)該重寫Thread類的run方法,在run方法中寫入需要在新線程中執(zhí)行的語句段。這個子類的對象需要調(diào)用start方法來啟動,新線程將自動進入run方法。原線程將同時繼續(xù)往下執(zhí)行Thread類直接繼承了Object類,并實現(xiàn)了Runnable接口。它位于java.lang包中,因而程序開頭不用import任何包就可直接使用。繼承Thread類實現(xiàn)Runnable接口實現(xiàn)Callable接口推薦這兩種方式,擴展性更好線程任務(wù)無返回值線程任務(wù)有返回值Java開啟線程的方式8.1.2Thread類8.1.2Thread類在新線程中完成計算某個整數(shù)的階乘classFactorialThreadextendsThread{privateintnum;publicFactorialThread(intnum){this.num=num;}publicvoidrun(){inti=num;intresult=1;System.out.println("newthreadstarted");while(i>0){ result=result*i; i=i-1;}System.out.println("Thefactorialof"+num+"is"+result);System.out.println("newthreadends");}}運行結(jié)果mainthreadstartsmainthreadendsnewthreadstartedThefactorialof10is3628800newthreadendspublicclassMultiTreadEx1{publicstaticvoidmain(String[]args){System.out.println("mainthreadstarts");FactorialThreadthread=newFactorialThread(10);thread.start();System.out.println("mainthreadends");}}8.1.2Thread類結(jié)果說明main線程已經(jīng)執(zhí)行完后,新線程才執(zhí)行完main方法調(diào)用thread.start()方法啟動新線程后并不等待其run方法返回就繼續(xù)運行,thread.run()方法在一邊獨自運行,不影響原來的main方法的運行源程序修改如果啟動新線程后希望主線程多持續(xù)一會再結(jié)束,可在start語句后加上讓當(dāng)前線程(這里當(dāng)然是main)休息100毫秒的語句:try{Thread.sleep(100);}catch(Exceptione){};§7.1.2Thread類修改后運行結(jié)果mainthreadstartsnewthreadstaredThefactorialof10is3628800newthreadendsmainthreadends運行結(jié)果說明新線程結(jié)束后main線程才結(jié)束publicclassMultiTreadEx1{publicstaticvoidmain(String[]args){System.out.println("mainthreadstarts");FactorialThreadthread=newFactorialThread(10);
thread.start();try{Thread.sleep(1);}catch(Exceptione){};System.out.println("mainthreadends");}}8.1.2Thread類實現(xiàn)多線程運行的基本方法-1可以直接從Thread類派生,重寫其run方法:
調(diào)用Thread類的start()方法啟動運行示例:ThreadRun.java注意主線程有一個“main”的名字,其它的線程名格式為“Thread-序號”。8.1.2Thread類創(chuàng)建3個新線程,每個線程睡眠一段時間(0~6秒),然后結(jié)束publicclassMultiTreadEx2{publicstaticvoidmain(String[]args){//創(chuàng)建并命名每個線程
TestThreadthread1=newTestThread("thread1");TestThreadthread2=newTestThread("thread2");TestThreadthread3=newTestThread("thread3");System.out.println("Startingthreads");thread1.start();//啟動線程1thread2.start();//啟動線程2thread3.start();//啟動線程3System.out.println("Threadsstarted,mainends\n");}}classTestThreadextendsThread{privateintsleepTime;publicTestThread(Stringname){super(name);sleepTime=(int)(Math.random()*6000);}publicvoidrun(){try{System.out.println(getName()+"goingtosleepfor"+sleepTime);Thread.sleep(sleepTime);//線程休眠
}catch(InterruptedExceptionexception){;}System.out.println(getName()+"finished");}}8.1.2Thread類運行結(jié)果StartingthreadsThreadsstarted,mainendsthread1goingtosleepfor3519thread2goingtosleepfor1689thread3goingtosleepfor5565thread2finishedthread1finishedthread3finished說明由于線程3休眠時間最長,所以最后結(jié)束,線程2休眠時間最短,所以最先結(jié)束每次運行,都會產(chǎn)生不同的隨機休眠時間,所以結(jié)果都不相同8.1.3Runnable接口實現(xiàn)多線程運行的基本方法-2將代碼封裝在Runable接口的run()方法內(nèi):8.1.3Runnable接口實現(xiàn)多線程運行的基本方法-2將代碼封裝在Runable接口的run()方法內(nèi):創(chuàng)建一個Runable對象:將Runable對象傳給Thread對象:啟動多線程運行:示例:RunableThreadTest.java主線程運行結(jié)束完畢,輔助線程仍在運行。只有兩個線程都運行完畢,進程才算終止。8.1.3Runnable接口Runnable接口Java多線程機制的一個重要部分,實際上它只有一個run()方法Thread類實現(xiàn)了Runnable接口,它更適合于多個線程處理同一資源實現(xiàn)Runnable接口的類的對象可以用來創(chuàng)建線程,這時start方法啟動此線程就會在此線程上運行run()方法在編寫復(fù)雜程序時相關(guān)的類可能已經(jīng)繼承了某個基類,而Java不支持多繼承,在這種情況下,便需要通過實現(xiàn)Runnable接口來生成多線程8.1.3Runnable接口使用Runnable接口實現(xiàn)例MultiTreadEx1功能classFactorialThreadimplementsRunnable{privateintnum;publicFactorialThread(intnum){this.num=num;}publicvoidrun(){inti=num;intresult=1;while(i>0){result=result*i;i=i-1;}System.out.println("Thefactorialof"+num+"is"+result);System.out.println("newthreadends");}}8.1.3Runnable接口publicclassMultiTreadEx3{publicstaticvoidmain(String[]args){System.out.println("mainthreadstarts");FactorialThreadt=newFactorialThread(10);
newThread(t).start();System.out.println("newthreadstarted,mainthreadends");}}8.1.3Runnable接口使用Runnable接口實現(xiàn)例MultiTreadEx2功能publicclassMultiTreadEx4{publicstaticvoidmain(String[]args){TestThreadthread1=newTestThread();TestThreadthread2=newTestThread();TestThreadthread3=newTestThread();System.out.println("Startingthreads");
newThread(thread1,"Thread1").start();newThread(thread2,"Thread2").start();newThread(thread3,"Thread3").start();
System.out.println("Threadsstarted,mainends\n");}}classTestThreadimplementsRunnable{privateintsleepTime;publicTestThread(){sleepTime=(int)(Math.random()*6000);}publicvoidrun(){try{System.out.println(
Thread.currentThread().getName()+"goingtosleepfor"+sleepTime);Thread.sleep(sleepTime);}catch(InterruptedExceptionexception){;}System.out.println(Thread.currentThread().getName()+"finished");}}63名稱說明publicThread()構(gòu)造一個新的線程對象,默認名為Thread-n,n是從0開始遞增的整數(shù)publicThread(Runnabletarget)構(gòu)造一個新的線程對象,以一個實現(xiàn)Runnable接口的類的對象為參數(shù)。默認名為Thread-n,n是從0開始遞增的整數(shù)publicThread(Stringname)構(gòu)造一個新的線程對象,并同時指定線程名publicThread(Runnabletask,Stringname)構(gòu)造一個新的線程對象,以一個實現(xiàn)Runnable接口的類的對象為參數(shù),并同時指定線程名publicstaticThreadcurrentThread()返回當(dāng)前正在運行的線程對象publicstaticvoidyield()使當(dāng)前線程對象暫停,允許別的線程開始運行publicstaticvoidsleep(longmillis)使當(dāng)前線程暫停運行指定毫秒數(shù),但此線程并不失去已獲得的鎖旗標(biāo)。8.1.4Thread類常用方法64publicvoidstart()啟動線程,JVM將調(diào)用此線程的run方法,結(jié)果是將同時運行兩個線程,當(dāng)前線程和執(zhí)行run方法的線程publicvoidrun()Thread的子類應(yīng)該重寫此方法,內(nèi)容應(yīng)為該線程應(yīng)執(zhí)行的任務(wù)。publicfinalvoidstop()停止線程運行,釋放該線程占用的對象鎖旗標(biāo)。publicvoidinterrupt()中斷此線程publicfinalvoidjoin()在當(dāng)前線程中加入調(diào)用join方法的線程A,直到線程A死亡才能繼續(xù)執(zhí)行當(dāng)前線程publicfinalvoidjoin(longmillis)在當(dāng)前線程中加入調(diào)用join方法的線程A,直到到達參數(shù)指定毫秒數(shù)或線程A死亡才能繼續(xù)執(zhí)行當(dāng)前線程8.1.4Thread類常用方法65publicfinalvoidsetPriority(int
newPriority)設(shè)置線程優(yōu)先級publicfinalvoidsetDaemon(Booleanon)設(shè)置是否為后臺線程,如果當(dāng)前運行線程均為后臺線程則JVM停止運行。這個方法必須在start()方法前使用publicvoidsetName(Stringname)更該本線程的名稱為指定參數(shù)publicfinalboolean
isAlive()測試線程是否處于活動狀態(tài),如果線程被啟動并且沒有死亡則返回true8.1.4Thread類常用方法8.1.4Thread類常用方法Thread類Run()方法的奧秘Thread類實現(xiàn)了Runnable接口,所以,Thread類也有Run()方法。Target字段在調(diào)用Thread類的構(gòu)造函數(shù)時被設(shè)置。當(dāng)提供了Runnable對象時,target引用它。當(dāng)沒有提供Runnable對象時,target為空。所以,如果我們從Thread類中派生子類,要重寫Run方法,在其中寫上“干活”的代碼8.1.4Thread類常用方法挑戰(zhàn)以下代碼將運行哪個run()方法?
示例:RunWhatMethod.java
8.1.4Thread類常用方法暫停線程的執(zhí)行可以使用Thread.sleep()方法,讓線程休眠指定的時間,從而拖慢線程的運行。注意:要捕獲InterruptedException異常Demo:
ThreadSleep.java8.1.4Thread類常用方法另一種暫停線程的方法使用TimeUnit枚舉類型的相應(yīng)方法,其好處是能直接指定各種不同的時間單位:納秒、微秒、毫秒、秒、分鐘、小時、天Demo:
ThreadSleep.java8.1.4Thread類常用方法中止一個線程當(dāng)線程方法執(zhí)行完畢時,線程自然中止當(dāng)線程中發(fā)生未捕獲的異常時,操作系統(tǒng)往往會結(jié)束整個進程其它線程請求中斷當(dāng)前線程的工作,當(dāng)前線程響應(yīng)這個請求而提前結(jié)束工作。8.1.4Thread類常用方法向線程發(fā)出中斷請求外界通過調(diào)用線程的interrupt()方法向線程發(fā)出中斷工作的請求,這時,線程對象內(nèi)部的一個“中斷狀態(tài)”標(biāo)記被設(shè)置。線程對象定期檢查這一標(biāo)記,以合適的方式中斷已經(jīng)運行的工作8.1.4Thread類常用方法向線程發(fā)出中斷請求如果線程自己要調(diào)用sleep()方法,由于此方法在運行時會清除掉“中斷狀態(tài)標(biāo)記”,并激發(fā)一個InterruptedException,所以,應(yīng)該采用以下代碼模板:中斷一個線程執(zhí)行的示例Demo:PrimeGenertor此示例不斷計算一個數(shù)是否是素數(shù),XX秒之后自動中斷。8.1.4Thread類常用方法Java線程優(yōu)先級JavathreadpriorityPriorityinrange1-10,默認值為5。線程創(chuàng)建時,子線程繼承父線程的優(yōu)先級線程創(chuàng)建完畢后,可以通過調(diào)用setPeriority方法改變優(yōu)先級。操作系統(tǒng)線程調(diào)度,根據(jù)優(yōu)先級進行非搶占調(diào)度:英國的紳士風(fēng)度,java中使用Thread類的yield()方法實現(xiàn)搶占式調(diào)度:優(yōu)先讓優(yōu)先級高的線程使用CPU,如果線程的優(yōu)先級相同,那么會隨機選擇一個(線程隨機性),Java使用的為搶占式調(diào)度。8.1.3Runnable接口實現(xiàn)多線程運行的基本方法-3實現(xiàn)Callable接口//1.編寫一個類實現(xiàn)Callable接口classMyCallableimplementsCallable<Integer>{//2.重寫call方法
@OverridepublicInteger
call()throwsException{//3.將線程任務(wù)代碼寫在call方法中
intsum=0;for(inti=1;i<=100;i++){sum+=i;System.out.println("sum="+sum);}returnsum;}}8.1.3Runnable接口實現(xiàn)多線程運行的基本方法-3實現(xiàn)Callable接口publicclassThreadDemo4{publicstaticvoidmain(String[]args)throwsException{
//4:
創(chuàng)建線程任務(wù)資源對象
MyCallablemc=newMyCallable();
//5:創(chuàng)建線程任務(wù)對象,封裝線程資源
FutureTask<Integer>task1=newFutureTask<>(mc);FutureTask<Integer>task2=newFutureTask<>(mc);
//6:創(chuàng)建線程對象,傳入線程任務(wù)
Threadt1=newThread(task1);Threadt2=newThread(task2);
//7:使用線程對象調(diào)用start開啟線程
t1.start();t2.start();Integerresult1=task1.get();Integerresult2=task2.get();System.out.println("task1獲取到的結(jié)果為:"+result1);System.out.println("task2獲取到的結(jié)果為:"+result2);}}8.2線程間的數(shù)據(jù)共享代碼共享多個線程的執(zhí)行代碼來自同一個類的run方法時,即稱它們共享相同的代碼數(shù)據(jù)共享當(dāng)共享訪問相同的對象時,即它們共享相同的數(shù)據(jù)使用Runnable接口可以輕松實現(xiàn)多個線程共享相同數(shù)據(jù),只要用同一個實現(xiàn)了Runnable接口的實例作為參數(shù)創(chuàng)建多個線程就可以了8.2線程間的數(shù)據(jù)共享修改例MultiTreadEx4,只用一個Runnable類型的對象為參數(shù)創(chuàng)建3個新線程。publicclassMultiTreadEx5{publicstaticvoidmain(String[]args){TestThreadthreadobj=newTestThread();System.out.println("Startingthreads");
newThread(threadobj,"Thread1").start();newThread(threadobj,"Thread2").start();newThread(threadobj,"Thread3").start();
System.out.println("Threadsstarted,mainends\n");}}classTestThreadimplementsRunnable{privateintsleepTime;publicTestThread(){sleepTime=(int)(Math.random()*6000);}publicvoidrun(){try{System.out.println(Thread.currentThread().getName()+"goingtosleepfor"+sleepTime);Thread.sleep(sleepTime);}catch(InterruptedExceptionexception){}; System.out.println(Thread.currentThread().getName()+"finished");}}8.2線程間的數(shù)據(jù)共享運行結(jié)果StartingthreadsThread1goingtosleepfor966Thread2goingtosleepfor966Threadsstarted,mainendsThread3goingtosleepfor966Thread1finishedThread2finishedThread3finished說明因為是用一個Runnable類型對象創(chuàng)建的3個新線程,這三個線程就共享了這個對象的私有成員sleepTime,在本次運行中,三個線程都休眠了966毫秒8.2線程間的數(shù)據(jù)共享獨立的同時運行的線程有時需要共享一些數(shù)據(jù)并且考慮到彼此的狀態(tài)和動作例如生產(chǎn)/消費問題:生產(chǎn)線程產(chǎn)生數(shù)據(jù)流,然后這些數(shù)據(jù)流再被消費線程消費假設(shè)一個Java應(yīng)用程序,其中有一個線程負責(zé)往文件寫數(shù)據(jù),另一個線程從同一個文件中往出都數(shù)據(jù),因為涉及到同一個資源,這里是同一個文件,這兩個線程必須保證某種方式的同步8.2線程間的數(shù)據(jù)共享用三個線程模擬三個售票口,總共出售200張票用3個線程模仿3個售票口的售票行為這3個線程應(yīng)該共享200張票的數(shù)據(jù)publicclassMultiTreadEx6{publicstaticvoidmain(String[]args){ SellTicketst=newSellTickets();newThread(t).start(); newThread(t).start(); newThread(t).start();}}classSellTicketsimplementsRunnable{privateinttickets=200;publicvoidrun(){
while(tickets>0){ System.out.println(Thread.currentThread().getName()+ "issellingticket"+tickets--); }}}8.2線程間的數(shù)據(jù)共享運行結(jié)果選最后幾行如下Thread-2issellingticket6Thread-1issellingticket5Thread-0issellingticket4Thread-2issellingticket3Thread-1issellingticket2Thread-0issellingticket1說明在這個例子中,創(chuàng)建了3個線程,每個線程調(diào)用的是同一個SellTickets對象中的run()方法,訪問的是同一個對象中的變量(tickets)如果是通過創(chuàng)建Thread類的子類來模擬售票過程,再創(chuàng)建3個新線程,則每個線程都會有各自的方法和變量,雖然方法是相同的,但變量卻是各有200張票,因而結(jié)果將會是各賣出200張票,和原意就不符了8.3多線程同步多線程程序的不確定性如果兩個線程同時運行:TwoThreadTest.java注意:只有調(diào)用Thread類的Start方法,才能真正地在一個獨立的線程中執(zhí)行代碼,直接調(diào)用Thread類的run方法,并不能啟動一個新的線程,代碼是在調(diào)用者線程中執(zhí)行的。8.3多線程同步同步方法與同步代碼塊為什么要同步線程?在多線程情況下,以上代碼有何問題?8.3多線程同步引發(fā)問題的根源解決方法:給要訪問數(shù)據(jù)的線程添加一個規(guī)定:一次只允許一個線程訪問數(shù)據(jù)。只有“當(dāng)前正在訪問數(shù)據(jù)”的線程結(jié)束訪問之后,其他線程才允許訪問這個數(shù)據(jù)。線程安全和同步安全問題出現(xiàn)的條件
是多線程環(huán)境
有共享數(shù)據(jù)
有多條語句操作共享數(shù)據(jù)同步技術(shù):
將多條語句操作共享數(shù)據(jù)的代碼給鎖起來,讓任意時刻只能有一個線程可以執(zhí)行同步代碼塊同步方法8.3多線程同步8.3多線程同步編寫線程安全的代碼——同步代碼塊亦可使用同步代碼塊達到相同的目的:TestThread1.javaTestThread2.java上述代碼中的object代表同步對象,通常我們會將要訪問同步對象的代碼放入到此同步塊中(這并非是必須遵守的規(guī)則,完全可以放置任意的代碼)。JVM會保證只有一個線程執(zhí)行代碼塊中的這些代碼。8.3多線程同步編寫線程安全的代碼——同步方法添加了synchronized關(guān)鍵字的方法稱為“同步方法”。每次只允許一個線程執(zhí)行同步方法。其余的線程阻塞,直到此方法執(zhí)行完畢當(dāng)方法執(zhí)行完畢,JVM調(diào)度下一個最高優(yōu)先級的線程運行。8.3多線程同步設(shè)計同步方法在實際開發(fā)中,通常在內(nèi)部放置一個專用于同步的對象,在同步方法內(nèi)部鎖定它:Demo:SynchronizedMethodTest.java8.3多線程同步同步對象引發(fā)的問題剖析示例:UseSynchronized.java實驗:鎖定同一對象實現(xiàn)實例方法的同步鎖定不同對象實現(xiàn)實例方法的同步如何實現(xiàn)實例方法與靜態(tài)方法的同步8.3多線程同步同步方法小結(jié)-1當(dāng)synchronized方法執(zhí)行完畢或發(fā)生異常時,會自動釋放鎖。被synchronized保護的數(shù)據(jù)應(yīng)該是私有的。多個線程對同一個對象的成員變量進行操作時,他們對該成員變量的操作是互相影響的。方法內(nèi)部所定義的局部變量,每個執(zhí)行它的線程都會有一個拷貝,訪問它是安全的。線程訪問synchronized靜態(tài)方法時,會鎖定其class對象,這將導(dǎo)致所有調(diào)用此靜態(tài)方法的線程“串行”執(zhí)行。8.3多線程同步同步方法小結(jié)-2當(dāng)synchronized方法是一種粗粒度的并發(fā)控制手段,某一時刻只能有一個線程執(zhí)行該方法。synchronized塊則是一種細粒度的并發(fā)控制,只會將塊中的代碼同步,位于方法內(nèi)synchronized塊之外的代碼是可以被多個線程同時訪問到。注意:不恰當(dāng)?shù)逆i定,可能會帶來嚴重的性能問題。8.3多線程同步讓多個線程協(xié)同工作……線程同步等待另一個線程的結(jié)束A線程正在運行,希望插入一個B線程,要求B先執(zhí)行完畢,A才繼續(xù)運行使用Join方法Join的含義:將某一線程加入成為另一個線程的流程之一。8.3多線程同步讓多個線程協(xié)同工作……線程同步等待另一個線程的結(jié)束A線程正在運行,希望插入一個B線程,要求B先執(zhí)行完畢,A才繼續(xù)運行使用Join方法Join的含義:將某一線程加入成為另一個線程的流程之一。示例:JoinTest.java。不使用join方法,將導(dǎo)致兩個線程的交錯執(zhí)行。8.3多線程同步有時線程之間彼此不獨立、需要同步線程間的互斥同時運行的幾個線程需要共享一個(些)數(shù)據(jù)一個線程對共享的數(shù)據(jù)進行操作時,不允許其他線程打斷它,否則會破壞數(shù)據(jù)的完整性。即被多個線程共享的數(shù)據(jù),在某一時刻只允許一個線程對其進行操作“生產(chǎn)者/消費者”問題生產(chǎn)者產(chǎn)生數(shù)據(jù),消費者消費數(shù)據(jù),具體來說,假設(shè)有一個Java應(yīng)用程序,其中有一個線程負責(zé)往數(shù)據(jù)區(qū)寫數(shù)據(jù),另一個線程從同一數(shù)據(jù)區(qū)中讀數(shù)據(jù),兩個線程可以并行執(zhí)行(類似于流水線上的兩道工序)如果數(shù)據(jù)區(qū)已滿,,生產(chǎn)者要等消費者取走一些數(shù)據(jù)后才能再放;而當(dāng)數(shù)據(jù)區(qū)沒有數(shù)據(jù)時,消費者要等生產(chǎn)者放入一些數(shù)據(jù)后再取8.3多線程同步用兩個線程模擬存票、售票過程假定開始售票處并沒有票,一個線程往里存票,另外一個線程則往出賣票我們新建一個票類對象,讓存票和售票線程都訪問它。本例采用兩個線程共享同一個數(shù)據(jù)對象來實現(xiàn)對同一份數(shù)據(jù)的操作publicclassMultiTreadEx7{publicstaticvoidmain(String[]args){ Ticketst=newTickets(10); newConsumer(t).start();
newProducer(t).start();
}}8.3多線程同步classTickets{intnumber=0;//票號
intsize;//總票數(shù)
booleanavailable=false;//表示目前是否有票可售
publicTickets(intsize)//構(gòu)造函數(shù),傳入總票數(shù)參數(shù)
{ this.size=size;} }8.3多線程同步classProducerextendsThread{Ticketst=null;publicProducer(Ticketst){ this.t=t;}publicvoidrun(){ while(t.number<t.size) { System.out.println("Producerputsticket"+(++t.number));t.available=true; } }}8.3多線程同步classConsumerextendsThread//售票線程{Ticketst=null;inti=0;publicConsumer(Ticketst){this.t=t;}publicvoidrun(){ while(i<t.size){ if(t.available==true&&i<=t.number) System.out.println("Consumerbuysticket"+(++i)); if(i==t.number) t.available=false; } }}8.3多線程同步運行結(jié)果Producerputsticket1Producerputsticket2Producerputsticket3Producerputsticket4Producerputsticket5Producerputsticket6Producerputsticket7Producerputsticket8Consumerbuysticket1Consumerbuysticket2Consumerbuysticket3Consumerbuysticket4Consumerbuysticket5Consumerbuysticket6Consumerbuysticket7Consumerbuysticket8Producerputsticket9Producerputsticket10Consumerbuysticket9Consumerbuysticket10.通過讓兩個線程操縱同一個票類對象,實現(xiàn)了數(shù)據(jù)共享的目的線程通信介紹確保線程能夠按照預(yù)定的順序執(zhí)行,并且能夠安全地訪問共享資源8.4線程通信線程通信介紹8.4線程通信線程通信介紹搶到執(zhí)行權(quán)8.4線程通信線程通信介紹搶到執(zhí)行權(quán)8.4線程通信線程通信介紹搶到執(zhí)行權(quán)嗝~不是...???!!你全吃了啊?8.4線程通信線程通信介紹8.4線程通信線程通信介紹搶到執(zhí)行權(quán)8.4線程通信線程通信介紹搶到執(zhí)行權(quán)8.4線程通信線程通信介紹搶到執(zhí)行權(quán)8.4線程通信線程通信介紹搶到執(zhí)行權(quán)8.4線程通信線程通信介紹搶到執(zhí)行權(quán)8.4線程通信線程通信介紹搶到執(zhí)行權(quán)8.4線程通信線程通信介紹搶到執(zhí)行權(quán)8.4線程通信線程通信介紹搶到執(zhí)行權(quán)8.4線程通信確保線程能夠按照預(yù)定的順序執(zhí)行
并且能夠安全地訪問共享資源使多條線程更好的進行協(xié)同工作等待喚醒機制成員方法說明voidwait()使當(dāng)前線程等待voidnotify();隨機喚醒單個等待的線程這些方法需要使用鎖對象調(diào)用注意事項8.4線程通信等待喚醒機制成員方法說明voidwait()使當(dāng)前線程等待voidnotify();隨機喚醒單個等待的線程voidnotifyAll();喚醒所有等待的線程這些方法需要使用鎖對象調(diào)用注意事項8.4線程通信等待喚醒機制成員方法說明voidwait()使當(dāng)前線程等待voidnotify();隨機喚醒單個等待的線程voidnotifyAll();喚醒所有等待的線程這些方法需要使用鎖對象調(diào)用注意事項wait()
方法在等待的時候會釋放鎖對象8.4線程通信等待喚醒機制成員方法說明voidawait()指定線程等待voidsignal();指定喚醒單個等待的線程使用ReentrantLock實現(xiàn)同步,并獲取Condition對象8.4線程通信Condition是一個接口,它提供了線程之間的協(xié)調(diào)機制,可以將它看作是一個更加靈活、更加強大的wait()和notify()機制,通常與Lock接口(比如ReentrantLock)一起使用。8.4線程通信importjava.util.concurrent.locks.Condition;importjava.util.concurrent.locks.ReentrantLock;ClassA{privatefinalReentrantLock
lock=newReentrantLock();privatefinalConditioncondition=lock.newCondition();
Publicvoidm(){
lock.lock(); try{//保證線程安全的代碼; } catch(InterruptedExceptione){ e.printStackTrace();}finally{
lock.unlock();//如果同步代碼有異常,要將unlock()寫入finally語句塊。}}}8.3線程通信需求:創(chuàng)建兩個線程,一個線程負責(zé)打印數(shù)組中的每個元素,另一個線程負責(zé)打印字母;兩個線程交替打印。數(shù)組中元素總數(shù)為5。8.3線程通信publicclassAlternatePrint1{privatestaticfinalObjectlock=newObject();privatestaticvolatileintindex=0;privatestaticvolatilecharletter='A';publicstaticvoidmain(String[]args){RunnableprintArrayRunnable=newPrintArrayRunnable();RunnableprintLetterRunnable=newPrintLetterRunnable();Threadthread1=newThread(printNumberRunnable);Threadthread2=newThread(printLetterRunnable);thread1.start();thread2.start();}8.3線程通信privatestaticclassPrintArrayRunnableimplementsRunnable{privateStringarr[]={"java","c語言","c++","python","go"};@Overridepublicvoidrun(){synchronized(lock){while(index<arr.length){System.out.print(arr[index]+"");index++;lock.notify();//喚醒等待的線程
try{lock.wait();//等待其他線程打印
}catch(InterruptedExceptione){e.printStackTrace();}}}}}8.3線程通信privatestaticclassPrintLetterRunnableimplementsRunnable{@Overridepublicvoidrun(){synchronized(lock){while(letter<='D'){System.out.print(letter+"");letter++;lock.notify();//喚醒等待的線程
try{lock.wait();//等待其他線程打印
}catch(InterruptedExceptione){e.printStackTrace();}}}}}}生產(chǎn)者消費者模式生產(chǎn)者消費者模式是一個十分經(jīng)典的多線程協(xié)作的模式主包含了兩類線程:
生產(chǎn)者線程,用于生產(chǎn)數(shù)據(jù)
消費者線程,用于消費數(shù)據(jù)8.4線程通信負責(zé)消費生產(chǎn)者消費者模式WareHouse負責(zé)生產(chǎn)Producerboolenmark=false;判斷包子的狀態(tài)false:生產(chǎn)true:等待判斷包子的狀態(tài)false:等待true:開吃Consumer8.4線程通信生產(chǎn)者消費者模式WareHouse負責(zé)生產(chǎn)負責(zé)消費ProducerConsumerboolenmark=false;判斷包子的狀態(tài)false:生產(chǎn)
改變mark狀態(tài)
喚醒消費者線程true:等待判斷包子的狀態(tài)false:等待true:開吃8.4線程通信生產(chǎn)者消費者模式WareHouse負責(zé)生產(chǎn)負責(zé)消費Producerboolenmark=false;判斷包子的狀態(tài)false:生產(chǎn)
改變mark狀態(tài)
喚醒消費者線程true:等待判斷包子的狀態(tài)false:等待true:開吃
改變mark狀態(tài)
喚醒生產(chǎn)者線程Consumer8.4線程通信生產(chǎn)者消費者模式生產(chǎn)者消費者模式是一個十分經(jīng)典的多線程協(xié)作的模式主包含了兩類線程:
生產(chǎn)者線程,用于生產(chǎn)數(shù)據(jù)
消費者線程,用于消費數(shù)據(jù)為了解耦生產(chǎn)者和消費者的關(guān)系,通常會采用共享的數(shù)據(jù)區(qū)域(緩沖區(qū)),就像是一個倉庫生產(chǎn)者生產(chǎn)數(shù)據(jù)之后直接放置在共享數(shù)據(jù)區(qū)中,并不需要關(guān)心消費者的行為消費者只需要從共享數(shù)據(jù)區(qū)中去獲取數(shù)據(jù),并不需要關(guān)心生產(chǎn)者的行為8.4線程通信8.5線程生命周期線程的生命周期線程從產(chǎn)生到消亡的過程一個線程在任何時刻都處于某種線程狀態(tài)(threadstate)8.5線程生命周期線程生命周期線程的啟動后的各種狀態(tài)線程被創(chuàng)建并啟動以后,它并不是一啟動就進入了執(zhí)行狀態(tài),也不是一直處于執(zhí)行狀態(tài)。線程對象在不同的時期有不同的狀態(tài)
狀態(tài)具體含義NEW(新建)創(chuàng)建線程對象RUNNABLE(就緒)start方法被調(diào)用,但是還沒有搶到CPU執(zhí)行權(quán)BLOCKED(阻塞)線程開始運行,但是沒有獲取到鎖對象WAITING(等待)wait方法TIMED_WAITING(計時等待)sleep方法TERMINATED(結(jié)束狀態(tài))代碼全部運行完畢8.5線程生命周期run();結(jié)束運行死亡阻塞無限等待計時等待就緒新建線程生命周期創(chuàng)建線程對象有執(zhí)行資格沒有執(zhí)行權(quán)沒有執(zhí)行資格沒有執(zhí)行權(quán)沒有執(zhí)行資格沒有執(zhí)行權(quán)線程死亡變成垃圾沒有執(zhí)行資格沒有執(zhí)行權(quán)有執(zhí)行資格有執(zhí)行權(quán)start();搶到CPU執(zhí)行權(quán)其他線程搶走CPU執(zhí)行權(quán)sleep(計時);到時間了wait();notify();無法獲取鎖獲取到鎖8.5線程生命周期死鎖線程在運行過程中,其中某個步驟往往需要滿足一些條件才能繼續(xù)進行下去,如果這個條件不能滿足,線程將在這個步驟上出現(xiàn)阻塞線程A可能會陷于對線程B的等待,而線程B同樣陷于對線程C的等待,依次類推,整個等待鏈最后又可能回到線程A。如此一來便陷入一個彼此等待的輪回中,任何線程都動彈不得,此即所謂死鎖(deadlock)對于死鎖問題,關(guān)鍵不在于出現(xiàn)問題后調(diào)試,而是在于預(yù)防8.5線程生命周期設(shè)想一個游戲,規(guī)則為3個人站在三角形的三個頂點的位置上,三個邊上放著三個球,如圖所示。每個人都必須先拿到自己左手邊的球,才能再拿到右手邊的球,兩手都有球之后,才能夠把兩個球都放下Player_0Player_1Player_20218.5線程生命周期結(jié)束線程的生命正常運行結(jié)束使用退出標(biāo)志退出線程interrupt方法結(jié)束線程;isInterrupted()stop()方法已棄用;(使用Thread.stop()可以立即終止線程,但這是一種非常危險的做法,因為它不保證資源的正常釋放,可能會導(dǎo)致程序狀態(tài)不一致,因此它已經(jīng)在Java中被棄用了。)8.5線程生命周期正常運行結(jié)束publicclassMyRunnableimplementsRunnable{publicvoidrun(){//模擬任務(wù)代碼
System.out.println("任務(wù)開始執(zhí)行");try{Thread.sleep(2000);//模擬耗時操作
}catch(InterruptedExceptione){Thread.currentThread().interrupt();//重新設(shè)置中斷狀態(tài)
}System.out.println("任務(wù)執(zhí)行結(jié)束");}}publicclassThreadEndDemo{publicstaticvoidmain(String[]args){Threadt=newThread(newMyRunnable());t.start();}}8.5線程生命周期使用退出標(biāo)志退出線程publicclassFlagRunnableimplementsRunnable{privatevolatilebooleanexit=false;publicvoidrun(){while(!exit){//...執(zhí)行任務(wù)
}System.out.println("退出標(biāo)志被設(shè)置,線程結(jié)束運行");}publicvoidsetExit(booleanexit){this.exit=exit;}}publicclassFlagThreadDemo{publicstaticvoidmain(String[]args){FlagRunnablerunnable=newFlagRunnable();Threadt=newThread(runnable);t.start();//在恰當(dāng)?shù)臅r刻設(shè)置退出標(biāo)志
runnable.setExit(true);}}8.5線程生命周期使用Interrupt方法正確終止線程publicclassInterruptedRunnableimplementsRunnable{publicvoidrun(){try{while(!Terrupted()){//...執(zhí)行復(fù)雜任務(wù)
//模擬長時間運行操作
Thread.sleep(5000);}}catch(InterruptedExceptione){System.out.println("線程被中斷");//可以在這里做一些資源清理操作
}finally{System.out.println("線程優(yōu)雅地結(jié)束運行");}}}publicclassInterruptDemo{publicstaticvoidmain(String[]args)throwsInterruptedException{Threadt=newThread(newInterruptedRunnable());t.start();//給線程一些時間執(zhí)行任務(wù)
Thread.sleep(2000);//請求中斷線程
errupt();}}8.6線程的優(yōu)先級線程調(diào)度在單CPU的系統(tǒng)中,多個線程需要共享CPU,在任何時間點上實際只能有一個線程在運行控制多個線程在同一個CPU上以某種順序運行稱為線程調(diào)度Java虛擬機支持一種非常簡單的、確定的調(diào)度算法,叫做固定優(yōu)先級算法。這個算法基于線程的優(yōu)先級對其進行調(diào)度8.6線程的優(yōu)先級線程的優(yōu)先級每個Java線程都有一個優(yōu)先級,其范圍都在1和10之間。默認情況下,每個線程的優(yōu)先級都設(shè)置為5在線程A運行過程中創(chuàng)建的新的線程對象B,初始狀態(tài)具有和線程A相同的優(yōu)先級如果A是個后臺線程,則B也是個后臺線程可在線程創(chuàng)建之后的任何時候,通過setPriority(intpriority)方法改變其原來的優(yōu)先級8.6線
溫馨提示
- 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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 甲狀腺功能考試題及答案
- 北京知識直播培訓(xùn)課件
- 2025年黨章知識競賽必考題及答案
- (2025年)黑龍江省牡丹江市全國計算機等級考試網(wǎng)絡(luò)技術(shù)預(yù)測試題含答案
- 循環(huán)系統(tǒng)護理試題(含答案)
- 護理查對制度試題及標(biāo)準(zhǔn)答案
- 2024年急性精神科科N2-N4護士理論知識考核試題(含答案)
- 2024年山東“安全生產(chǎn)月”知識主題試題附參考答案
- 2024年全國RDPAC資格認證考試題庫(附含答案)
- 2025年育嬰師三級(高級育嬰師)從業(yè)資格證考試內(nèi)容及答案
- 2025年《農(nóng)產(chǎn)品質(zhì)量安全法》知識點考試題庫資料及答案
- 智人擴散路徑重構(gòu)-洞察及研究
- 三方委托付工程款協(xié)議書
- 信通員考試試題及答案
- 四川成都成華區(qū)龍?zhí)督值擂k事處招聘編外聘用制工作人員筆試模擬試題及答案詳解1套
- 有限空間安全作業(yè)培訓(xùn)試題(含答案)
- 物業(yè)應(yīng)急管理辦法
- 設(shè)備調(diào)劑管理辦法
- 藍天救援隊規(guī)定管理制度
- 銀監(jiān)會手機租賃管理辦法
- 常見上肢骨折護理常規(guī)
評論
0/150
提交評論