Java語(yǔ)言程序設(shè)計(jì)(第三版-清華)第8章new_第1頁(yè)
Java語(yǔ)言程序設(shè)計(jì)(第三版-清華)第8章new_第2頁(yè)
Java語(yǔ)言程序設(shè)計(jì)(第三版-清華)第8章new_第3頁(yè)
Java語(yǔ)言程序設(shè)計(jì)(第三版-清華)第8章new_第4頁(yè)
Java語(yǔ)言程序設(shè)計(jì)(第三版-清華)第8章new_第5頁(yè)
已閱讀5頁(yè),還剩62頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

第九章線程1目錄Java中的線程線程的生命周期Thread的子類創(chuàng)立線程使用Runable接口線程的常用方法線程的優(yōu)先級(jí)線程同步在同步方法中使用wait()、notify和notifyAll()方法掛起、恢復(fù)和終止線程本章小結(jié)2線程的概念進(jìn)程和線程的區(qū)別〔Example9_11)進(jìn)程一個(gè)獨(dú)立程序的每一次運(yùn)行稱為一個(gè)進(jìn)程,例如用字處理軟件編輯文稿時(shí),同時(shí)翻開(kāi)mp3播放程序聽(tīng)音樂(lè),這兩個(gè)獨(dú)立的程序在同時(shí)運(yùn)行,稱為兩個(gè)進(jìn)程設(shè)置一個(gè)進(jìn)程要占用相當(dāng)一局部處理器時(shí)間和內(nèi)存資源大多數(shù)操作系統(tǒng)不允許進(jìn)程訪問(wèn)其他進(jìn)程的內(nèi)存空間,進(jìn)程間的通信很不方便,編程模型比較復(fù)雜進(jìn)程通信方式共享存儲(chǔ)器系統(tǒng)、消息傳到機(jī)制、管道通信3線程一個(gè)程序中多段代碼同時(shí)并發(fā)執(zhí)行,稱為多線程通過(guò)多線程,一個(gè)進(jìn)程外表上看同時(shí)可以執(zhí)行一個(gè)以上的任務(wù)——并發(fā)創(chuàng)立線程比創(chuàng)立進(jìn)程開(kāi)銷要小得多,線程之間的協(xié)作和數(shù)據(jù)交換也比較容易(進(jìn)程獨(dú)站資源)線程間共享資源〔內(nèi)存、代碼、數(shù)據(jù)〕有利于并行處理線程的概念(續(xù))4線程的狀態(tài)與生命周期新建:當(dāng)一個(gè)Thread類或其子類的對(duì)象被聲明并創(chuàng)立時(shí),新生的線程對(duì)象處于新建狀態(tài)。此時(shí)它已經(jīng)有了相應(yīng)的內(nèi)存空間和其他資源。就緒狀態(tài):線程等待CPU運(yùn)行狀態(tài):start()方法開(kāi)始執(zhí)行中斷(阻塞)狀態(tài):sleep(),wait(),I/O完成or發(fā)布I/O請(qǐng)求sleep():使當(dāng)前線程進(jìn)入等待狀態(tài),參數(shù)設(shè)定其等待時(shí)間wait():使當(dāng)前線程進(jìn)入等待狀態(tài),調(diào)用nofify(),ornotifyAll(),使其重新進(jìn)入線程等待隊(duì)列死亡:run()方法完成stop()方法被調(diào)用5LifeCycleofThread6誕生狀態(tài)線程剛剛被創(chuàng)立就緒狀態(tài)線程的start方法已被執(zhí)行線程已準(zhǔn)備好運(yùn)行運(yùn)行狀態(tài)處理機(jī)分配給了線程,線程正在運(yùn)行阻塞狀態(tài)〔Blocked〕在線程發(fā)出輸入/輸出請(qǐng)求且必須等待其返回遇到用synchronized標(biāo)記的方法而未獲得其監(jiān)視器暫時(shí)不能進(jìn)入執(zhí)行時(shí)休眠狀態(tài)〔Sleeping〕執(zhí)行sleep方法而進(jìn)入休眠死亡狀態(tài)線程已完成或退出線程的幾種根本狀態(tài)(續(xù))7主線程:Java應(yīng)用程序總是從主類的main方法開(kāi)始執(zhí)行。當(dāng)JVM加載代碼,發(fā)現(xiàn)main方法之后,啟動(dòng)的線程稱作“主線程”,該線程負(fù)責(zé)執(zhí)行main方法。在main方法的執(zhí)行中再創(chuàng)立的線程,就稱為程序中的其它線程。如果main方法中沒(méi)有創(chuàng)立其他的線程,那么當(dāng)main方法執(zhí)行完最后一個(gè)語(yǔ)句,JVM就會(huì)結(jié)束Java應(yīng)用程序。如果main方法中又創(chuàng)立了其他線程,那么JVM就要在主線程和其他線程之間輪流切換,main方法即使執(zhí)行完最后的語(yǔ)句,JVM也不會(huì)結(jié)束程序,JVM一直要等到程序中的所有線程都結(jié)束之后,才結(jié)束Java應(yīng)用程序。8Java中創(chuàng)立線程的兩種方法Thread類的子類創(chuàng)立線程對(duì)象,子類重寫(xiě)Thread類中的run()方法使用Thread類直接創(chuàng)立線程對(duì)象,但需要使用Runable接口9Thread類Thread類在Java程序中創(chuàng)立多線程的方法之一是繼承Thread類封裝了Java程序中一個(gè)線程對(duì)象需要擁有的屬性和方法從Thread類派生一個(gè)子類,并創(chuàng)立這個(gè)子類的對(duì)象,就可以產(chǎn)生一個(gè)新的線程。這個(gè)子類應(yīng)該重寫(xiě)Thread類的run〔〕方法,在run方法中寫(xiě)入需要在新線程中執(zhí)行的語(yǔ)句段。這個(gè)子類的對(duì)象需要調(diào)用start方法來(lái)啟動(dòng),新線程將自動(dòng)進(jìn)入run方法。原線程將同時(shí)繼續(xù)往下執(zhí)行它位于java.lang包中,因而程序開(kāi)頭不用import任何包就可直接使用例子:Example9_1.java10publicclassExample9_1{publicstaticvoidmain(Stringargs[]){Lefthandleft;Righthandright;left=newLefthand();//創(chuàng)立線程right=newRighthand();left.start();right.start();for(inti=1;i<=3;i++){("我是主線程");}}}classLefthandextendsThread{publicvoidrun(){for(inti=1;i<=3;i++){("我是左手線程");}}}classRighthandextendsThread{publicvoidrun(){for(inti=1;i<=3;i++){("我是右手線程");}}}我是主線程我是主線程我是主線程我是左手線程我是左手線程我是左手線程我是右手線程我是右手線程我是右手線程11Thread類(續(xù))

在新線程中完成計(jì)算某個(gè)整數(shù)的階乘publicclassEx8_1{publicstaticvoidmain(String[]args){ System.out.println("mainthreadstarts"); FactorialThreadthread=newFactorialThread(10);

thread.start();

System.out.println("mainthreadends");}}classFactorialThreadextends

Thread{privateintnum;publicFactorialThread(intnum){this.num=num;

}

12publicvoidrun(){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");}}運(yùn)行結(jié)果mainthreadstartsmainthreadendsnewthreadstartedThefactorialof10is3628800newthreadendsThread類(續(xù))

13結(jié)果說(shuō)明main線程已經(jīng)執(zhí)行完后,新線程才執(zhí)行完main函數(shù)調(diào)用thread.start()方法啟動(dòng)新線程后并不等待其run方法返回就繼續(xù)運(yùn)行(執(zhí)行()),thread.run函數(shù)在一邊單獨(dú)運(yùn)行,不影響原來(lái)的main函數(shù)的運(yùn)行源程序修改如果啟動(dòng)新線程后希望主線程多持續(xù)一會(huì)再結(jié)束,可在start語(yǔ)句后加上讓當(dāng)前線程〔main〕休息1毫秒的語(yǔ)句:try{Thread.sleep(1);}catch(Exceptione){};Thread類(續(xù))

14修改后運(yùn)行結(jié)果mainthreadstartsnewthreadstaredThefactorialof10is3628800newthreadendsmainthreadends運(yùn)行結(jié)果說(shuō)明新線程結(jié)束后main線程才結(jié)束例子Ex8_1.javaThread類(續(xù))

——例8_1修改后運(yùn)行結(jié)果15Thread類(續(xù))

——常用API函數(shù)名稱說(shuō)明publicThread()構(gòu)造一個(gè)新的線程對(duì)象,默認(rèn)名為T(mén)hread-n,n是從0開(kāi)始遞增的整數(shù)publicThread(Runnabletarget)構(gòu)造一個(gè)新的線程對(duì)象,以一個(gè)實(shí)現(xiàn)Runnable接口的類的對(duì)象為參數(shù)。默認(rèn)線程名為T(mén)hread-n,n是從0開(kāi)始遞增的整數(shù)publicThread(Stringname)構(gòu)造一個(gè)新的線程對(duì)象,并同時(shí)指定線程名publicstaticThreadcurrentThread()返回當(dāng)前正在運(yùn)行的線程對(duì)象publicstaticvoidyield()使當(dāng)前線程對(duì)象暫停,允許別的線程開(kāi)始運(yùn)行publicstaticvoidsleep(longmillis)使當(dāng)前線程暫停運(yùn)行指定毫秒數(shù),但此線程并不失去已獲得的鎖旗標(biāo)。16publicvoidstart()啟動(dòng)線程,JVM將調(diào)用此線程的run方法,結(jié)果是將同時(shí)運(yùn)行兩個(gè)線程,當(dāng)前線程和執(zhí)行run方法的線程publicvoidrun()Thread的子類應(yīng)該重寫(xiě)此方法,內(nèi)容應(yīng)為該線程應(yīng)執(zhí)行的任務(wù)。publicfinalvoidstop()停止線程運(yùn)行,釋放該線程占用的對(duì)象鎖旗標(biāo)。publicvoidinterrupt()打斷此線程publicfinalvoidjoin()在當(dāng)前線程中加入調(diào)用join方法的線程A,直到線程A死亡才能繼續(xù)執(zhí)行當(dāng)前線程publicfinalvoidjoin(longmillis)在當(dāng)前線程中加入調(diào)用join方法的線程A,直到到達(dá)參數(shù)指定毫秒數(shù)或線程A死亡才能繼續(xù)執(zhí)行當(dāng)前線程Thread類(續(xù))

——常用API函數(shù)17publicfinalvoidsetPriority(intnewPriority)設(shè)置線程優(yōu)先級(jí)publicfinalvoidsetDaemon(Booleanon)設(shè)置是否為后臺(tái)線程,如果當(dāng)前運(yùn)行線程均為后臺(tái)線程則JVM停止運(yùn)行。這個(gè)方法必須在start()方法前使用publicfinalvoidcheckAccess()判斷當(dāng)前線程是否有權(quán)力修改調(diào)用此方法的線程publicvoidsetName(Stringname)更改本線程的名稱為指定參數(shù)publicfinalbooleanisAlive()測(cè)試線程是否處于活動(dòng)狀態(tài),如果線程被啟動(dòng)并且沒(méi)有死亡則返回trueThread類(續(xù))

——常用API函數(shù)18創(chuàng)立3個(gè)新線程,每個(gè)線程睡眠一段時(shí)間〔0~6秒〕,然后結(jié)束publicclassEx8_2{publicstaticvoidmain(String[]args){//創(chuàng)立并命名每個(gè)線程TestThreadthread1=newTestThread("thread1");TestThreadthread2=newTestThread("thread2");TestThreadthread3=newTestThread("thread3");("Startingthreads");thread1.start();//啟動(dòng)線程1thread2.start();//啟動(dòng)線程2thread3.start();//啟動(dòng)線程3("Threadsstarted,mainends\n");}}Thread類(續(xù))

——例8_219classTestThreadextendsThread{privateintsleepTime;publicTestThread(Stringname){super(name);//調(diào)用父類構(gòu)造函數(shù)為線程命名

sleepTime=(int)(Math.random()*6000);

}publicvoidrun(){try{(getName()+"goingtosleepfor"+sleepTime);

Thread.sleep(sleepTime);//線程休眠

}catch(InterruptedExceptionexception){};(getName()+"finished"}}Thread類(續(xù))

——例8_220運(yùn)行結(jié)果StartingthreadsThreadsstarted,mainendsthread1goingtosleepfor3519thread2goingtosleepfor1689thread3goingtosleepfor5565thread2finishedthread1finishedthread3finished說(shuō)明由于線程3休眠時(shí)間最長(zhǎng),所以最后結(jié)束,線程2休眠時(shí)間最短,所以最先結(jié)束每次運(yùn)行,都會(huì)產(chǎn)生不同的隨機(jī)休眠時(shí)間,所以結(jié)果都不相同Thread類(續(xù))

——例8_2運(yùn)行結(jié)果21Runnable接口Runnable接口在編寫(xiě)復(fù)雜程序時(shí)相關(guān)的類可能已經(jīng)繼承了某個(gè)基類,而Java不支持多繼承,在這種情況下,便需要通過(guò)實(shí)現(xiàn)Runnable接口來(lái)生成多線使用Thread創(chuàng)立線程對(duì)象時(shí),通常使用的構(gòu)造方法是:Thread〔Runnabletarget〕該構(gòu)造方法中的參數(shù)是一個(gè)Runnable類型的接口,因此,在創(chuàng)立線程

對(duì)象時(shí)必須向構(gòu)造方法的參數(shù)傳遞一個(gè)實(shí)現(xiàn)Runnable接口類的實(shí)例,

該實(shí)例對(duì)象稱作所創(chuàng)線程的目標(biāo)對(duì)象.當(dāng)線程調(diào)用start方法后,一旦輪到它來(lái)享用CPU資源,目標(biāo)對(duì)象就會(huì)自動(dòng)調(diào)用接口中的run方法〔接口回調(diào)〕.對(duì)于使用同一目標(biāo)對(duì)象的線程,目標(biāo)對(duì)象的成員變量自然就是這些線程的共享數(shù)據(jù)單元不同run()方法中的局部變量互不干擾。修改Ex8_1.java

例子:workspace/ThreadDemo2_2.java〔java2006/example9/ThreadDemo1_1,ThreadDemo2_2〕例子:Example9_3.javaTestThreadextendsThread{…..}TestThreadimplementsRunnable{newThread(Runnableth).start}22使用Runnable接口實(shí)現(xiàn)Ex8_1功能(使用一個(gè)目標(biāo)對(duì)象)publicclassEx8_1{publicstaticvoidmain(String[]args){System.out.println("mainthreadstarts");FactorialThreadt=newFactorialThread(10);newThread(t).start();//創(chuàng)立Thread對(duì)象System.out.println("newthreadstarted,mainthreadends");}}Runnable接口(續(xù))

——例8_1_1Runnable接口類的實(shí)例23classFactorialThreadimplementsRunnable{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");}}Runnable接口(續(xù))

——例8_324使用Runnable接口實(shí)現(xiàn)例8_2功能〔使用不同目標(biāo)對(duì)象〕publicclassEx8_4{publicstaticvoidmain(String[]args){ TestThreadthread1=newTestThread();TestThreadthread2=newTestThread();TestThreadthread3=newTestThread();("Startingthreads");

newThread(thread1,"Thread1").start();newThread(thread2,"Thread2").start();newThread(thread3,"Thread3").start();

("Threadsstarted,mainends\n");}}Runnable接口(續(xù))

——例8_4傳遞一個(gè)實(shí)現(xiàn)Runnable接口類的實(shí)例,并啟動(dòng)線程25classTestThreadimplementsRunnable{privateintsleepTime;publicTestThread()

{sleepTime=(int)(Math.random()*6000);

}publicvoidrun(){try{(

Thread.currentThread().getName()+"goingtosleepfor"+sleepTime);

Thread.sleep(sleepTime);

}catch(InterruptedExceptionexception){};

(Thread.currentThread().getName()+"finished");

}}Runnable接口(續(xù))

——例8_4StartingthreadsThreadsstarted,mainendsThread2goingtosleepfor1498Thread1goingtosleepfor4544Thread3goingtosleepfor3251Thread2finishedThread3finishedThread1finished26線程間的數(shù)據(jù)共享代碼共享多個(gè)線程的執(zhí)行代碼來(lái)自同一個(gè)類的run方法時(shí),即稱它們共享相同的代碼數(shù)據(jù)共享當(dāng)共享訪問(wèn)相同的對(duì)象時(shí),即它們共享相同的數(shù)據(jù)不同線程的run()方法中的局部變量互不干擾〔Example9_5_1.java〕使用Runnable接口可以輕松實(shí)現(xiàn)多個(gè)線程共享相同數(shù)據(jù),只要用同一個(gè)實(shí)現(xiàn)了Runnable接口的實(shí)例作為參數(shù)創(chuàng)立多個(gè)線程即可〔Ex8_5.java,p233-例9.3〕27classExample9_5_1{public

static

voidmain(Stringargs[]){

Movemove=newMove();

newThread(move,"zhangsan").start();

newThread(move,"lisi").start();}}28classMoveimplementsRunnable{

//inti=0;

public

voidrun(){inti=0;

while(i<=5){if(Thread.currentThread().getName().equals("zhangsan")){i=i+1;System.out.println(Thread.currentThread().getName()+"線程的局部變量i="+i);}

else

if(Thread.currentThread().getName().equals("lisi")){i=i+1;System.out.println(Thread.currentThread().getName()+"線程的局部變量i="+i);}

try{Thread.sleep(800);}

catch(InterruptedExceptione){}}}}29修改例8_4,只用一個(gè)Runnable類型的對(duì)象為參數(shù)創(chuàng)立3個(gè)新線程。publicclassEx8_5{publicstaticvoidmain(String[]args){ TestThreadthreadobj=newTestThread();("Startingthreads");

newThread(threadobj,"Thread1").start();newThread(threadobj,"Thread2").start();newThread(threadobj,"Thread3").start();

("Threadsstarted,mainends\n");}}線程間的數(shù)據(jù)共享(續(xù))

——例8_530classTestThreadimplementsRunnable{ privateintsleepTime; publicTestThread()

{sleepTime=(int)(Math.random()*6000);

} publicvoidrun()

{try{(Thread.currentThread().getName()+"goingtosleepfor"+ sleepTime);Thread.sleep(sleepTime);

}catch(InterruptedExceptionexception){}; (Thread.currentThread().getName()+"finished"); }}線程間的數(shù)據(jù)共享(續(xù))

——例8_531運(yùn)行結(jié)果StartingthreadsThread1goingtosleepfor966Thread2goingtosleepfor966Threadsstarted,mainendsThread3goingtosleepfor966Thread1finishedThread2finishedThread3finished說(shuō)明因?yàn)槭怯靡粋€(gè)Runnable類型對(duì)象創(chuàng)立的3個(gè)新線程,這三個(gè)線程就共享了這個(gè)對(duì)象的私有成員sleepTime,在本次運(yùn)行中,三個(gè)線程都休眠了966毫秒線程間的數(shù)據(jù)共享(續(xù))

——例8_5運(yùn)行結(jié)果32publicclassEx8_6{ publicstaticvoidmain(String[]args){ SellTicketst=newSellTickets();newThread(t).start();

newThread(t).start(); newThread(t).start(); }}線程間的數(shù)據(jù)共享(實(shí)際應(yīng)用)

——例8_6用三個(gè)線程模擬三個(gè)售票口,總共出售200張票-用3個(gè)線程模仿3個(gè)售票口的售票行為-這3個(gè)線程應(yīng)該共享200張票的數(shù)據(jù)33classSellTicketsimplementsRunnable{ privateinttickets=200;

publicvoidrun(){

while(tickets>0){ (Thread.currentThread().getName()+ "issellingticket"+tickets--); } }}線程間的數(shù)據(jù)共享(續(xù))

——例8_634運(yùn)行結(jié)果選最后幾行如下Thread-2issellingticket6Thread-1issellingticket5Thread-0issellingticket4Thread-2issellingticket3Thread-1issellingticket2Thread-0issellingticket1說(shuō)明在這個(gè)例子中,創(chuàng)立了3個(gè)線程,每個(gè)線程調(diào)用的是同一個(gè)SellTickets對(duì)象中的run()方法,訪問(wèn)的是同一個(gè)對(duì)象中的變量〔tickets〕如果是通過(guò)創(chuàng)立Thread類的子類來(lái)模擬售票過(guò)程,再創(chuàng)立3個(gè)新線程,那么每個(gè)線程都會(huì)有各自的方法和變量,雖然方法是相同的,但變量卻是各有200張票,因而結(jié)果將會(huì)是各賣出200張票,和原意就不符了線程間的數(shù)據(jù)共享(續(xù))

——例8_6運(yùn)行結(jié)果35publicvoidstart()啟動(dòng)線程,JVM將調(diào)用此線程的run方法,結(jié)果是將同時(shí)運(yùn)行兩個(gè)線程,當(dāng)前線程和執(zhí)行run方法的線程publicvoidrun()Thread的子類應(yīng)該重寫(xiě)此方法,內(nèi)容應(yīng)為該線程應(yīng)執(zhí)行的任務(wù)。publicfinalvoidstop()停止線程運(yùn)行,釋放該線程占用的對(duì)象鎖旗標(biāo)。publicvoidinterrupt()打斷此線程publicfinalvoidjoin()在當(dāng)前線程中加入調(diào)用join方法的線程A,直到線程A死亡才能繼續(xù)執(zhí)行當(dāng)前線程publicfinalvoidjoin(longmillis)在當(dāng)前線程中加入調(diào)用join方法的線程A,直到到達(dá)參數(shù)指定毫秒數(shù)或線程A死亡才能繼續(xù)執(zhí)行當(dāng)前線程Thread類(續(xù))

——常用API函數(shù)36interrupt()方法舉例publicclassExample9_9{publicstaticvoidmain(Stringargs[]){Aa=newA();();();}}classAimplementsRunnable{Threadstudent,teacher;A(){teacher=newThread(this);student=newThread(this);teacher.setName("王教授");student.setName("張三");}37

publicvoidrun(){if(Thread.currentThread()==student){try{System.out.println(student.getName()+"正在睡覺(jué),不聽(tīng)課");Thread.sleep(1000*60*60);}catch(InterruptedExceptione){System.out.println(student.getName()+"被老師叫醒了");}System.out.println(student.getName()+"開(kāi)始聽(tīng)課");}38

elseif(Thread.currentThread()==teacher){for(inti=1;i<=3;i++){("上課!");try{Thread.sleep(500);}catch(InterruptedExceptione){}}

errupt();//吵醒student}}}interrupt()方法使student線程發(fā)生InterruptException,從而結(jié)束休眠39GUI線程Example9_1140多線程的同步控制有時(shí)線程之間彼此不獨(dú)立、需要同步(相互配合)線程間的互斥同時(shí)運(yùn)行的幾個(gè)線程需要共享一個(gè)〔些〕數(shù)據(jù)一個(gè)線程對(duì)共享的數(shù)據(jù)進(jìn)行操作時(shí),不允許其他線程打斷它,否那么會(huì)破壞數(shù)據(jù)的完整性。即被多個(gè)線程共享的數(shù)據(jù),在某一時(shí)刻只允許一個(gè)線程對(duì)其進(jìn)行操作“生產(chǎn)者/消費(fèi)者”問(wèn)題〔工資管理員/雇員〕生產(chǎn)者產(chǎn)生數(shù)據(jù),消費(fèi)者消費(fèi)數(shù)據(jù),具體來(lái)說(shuō),假設(shè)有一個(gè)Java應(yīng)用程序,其中有一個(gè)線程負(fù)責(zé)往數(shù)據(jù)區(qū)寫(xiě)數(shù)據(jù),另一個(gè)線程從同一數(shù)據(jù)區(qū)中讀數(shù)據(jù),兩個(gè)線程可以并行執(zhí)行〔類似于流水線上的兩道工序〕如果數(shù)據(jù)區(qū)已滿,,生產(chǎn)者要等消費(fèi)者取走一些數(shù)據(jù)后才能再放;而當(dāng)數(shù)據(jù)區(qū)沒(méi)有數(shù)據(jù)時(shí),消費(fèi)者要等生產(chǎn)者放入一些數(shù)據(jù)后再取41線程同步的概念,包括互斥和協(xié)作互斥:許多線程在同一個(gè)共享數(shù)據(jù)上操作而互不干擾,同一時(shí)刻只能有一個(gè)線程訪問(wèn)該共享數(shù)據(jù)。因此有些方法或程序段在同一時(shí)刻只能被一個(gè)線程執(zhí)行,稱之為監(jiān)視區(qū)〔臨界區(qū)〕協(xié)作:多個(gè)線程可以有條件地同時(shí)操作共享數(shù)據(jù)。執(zhí)行監(jiān)視區(qū)代碼的線程在條件滿足的情況下可以允許其它線程進(jìn)入監(jiān)視區(qū)〔臨界區(qū)〕多線程的同步控制(續(xù))

——線程同步(Synchronization)42

ProducerandConsumerHolderProducerInput-Dataq0q0…qnq1ProducerInput-Dataq1ConsumerOutputDataqn+1ProducerInput-Dataqn+143synchronized——線程同步關(guān)鍵字把需要修改數(shù)據(jù)的方法用關(guān)鍵字synchronized來(lái)修飾,用于指定需要同步的代碼段或方法,也就是監(jiān)視區(qū)當(dāng)一個(gè)線程A使用一個(gè)synchronized修飾的方法時(shí),其他線程想使用這個(gè)方法時(shí)就必須等待,直到線程A使用完該方法〔除非線程A使用wait主動(dòng)讓出CUP資源〕.當(dāng)被synchronized限定的代碼段執(zhí)行完,就釋放鎖旗標(biāo)〔信號(hào)量〕例子:Example9_13.java多線程的同步控制(續(xù))

——synchronized關(guān)鍵字44線程間的通信為了更有效地協(xié)調(diào)不同線程的工作,需要在線程間建立溝通渠道,通過(guò)線程間的“對(duì)話”來(lái)解決線程間的同步問(wèn)題類的一些方法為線程間的通訊提供了有效手段一個(gè)線程在使用的同步方法中時(shí),可能根據(jù)問(wèn)題的需要,必須使用wait()(掛起)方法使本線程等待,暫時(shí)讓出CPU的使用權(quán),并允許其它線程使用這個(gè)同步方法。其它線程如果在使用這個(gè)同步方法時(shí)如果不需要等待,那么它用完這個(gè)同步方法的同時(shí),應(yīng)當(dāng)執(zhí)行notify(),notifyAll()〔恢復(fù)〕方法通知所有的由于使用這個(gè)同步方法而處于等待的線程結(jié)束等待。例子:Example9_14.java,Ex8_8.java,Ex8_9.java45線程的優(yōu)先級(jí)每個(gè)Java線程都有一個(gè)優(yōu)先級(jí),其范圍都在1和10之間。默認(rèn)情況下,每個(gè)線程的優(yōu)先級(jí)都設(shè)置為5在線程A運(yùn)行過(guò)程中創(chuàng)立的新的線程對(duì)象B,初始狀態(tài)具有和線程A相同的優(yōu)先級(jí)如果A是個(gè)后臺(tái)線程,那么B也是個(gè)后臺(tái)線程可在線程創(chuàng)立之后的任何時(shí)候,通過(guò)setPriority(intpriority)方法改變其原來(lái)的優(yōu)先級(jí)線程的優(yōu)先級(jí)(續(xù))46基于線程優(yōu)先級(jí)的線程調(diào)度具有較高優(yōu)先級(jí)的線程比優(yōu)先級(jí)較低的線程優(yōu)先執(zhí)行對(duì)具有相同優(yōu)先級(jí)的線程,Java的處理是隨機(jī)的底層操作系統(tǒng)支持的優(yōu)先級(jí)可能要少于10個(gè),這樣會(huì)造成一些混亂。因此,只能將優(yōu)先級(jí)作為一種很粗略的工具使用。最后的控制可以通過(guò)明智地使用yield()函數(shù)來(lái)完成我們只能基于效率的考慮來(lái)使用線程優(yōu)先級(jí),而不能依靠線程優(yōu)先級(jí)來(lái)保證算法的正確性線程的優(yōu)先級(jí)(續(xù))47假設(shè)某線程正在運(yùn)行,那么只有出現(xiàn)以下情況之一,才會(huì)使其暫停運(yùn)行一個(gè)具有更高優(yōu)先級(jí)的線程變?yōu)榫途w狀態(tài)〔Ready〕;由于輸入/輸出〔或其他一些原因〕、調(diào)用sleep、wait、yield方法使其發(fā)生阻塞;對(duì)于支持時(shí)間分片的系統(tǒng),時(shí)間片的時(shí)間期滿線程的優(yōu)先級(jí)(續(xù))48創(chuàng)立兩個(gè)具有不同優(yōu)先級(jí)的線程,都從1遞增到400000,每增加50000顯示一次publicclassEx8_13{publicstaticvoidmain(String[]args){ TestThread[]runners=newTestThread[2];for(inti=0;i<2;i++)runners[i]=newTestThread(i);runners[0].setPriority(2);//設(shè)置第一個(gè)線程優(yōu)先級(jí)為2runners[1].setPriority(3);//設(shè)置第二個(gè)線程優(yōu)先級(jí)為3for(inti=0;i<2;i++)runners[i].start();}}線程的優(yōu)先級(jí)(續(xù))

——例8_1349classTestThreadextendsThread{ privateinttick=1; privateintnum; publicTestThread(inti){this.num=i; } publicvoidrun(){ while(tick<400000){tick++;if((tick%50000)==0){//每隔5000進(jìn)行顯示

("Thread#"+num+",tick="+tick);yield();//放棄執(zhí)行權(quán)

}}} }線程的優(yōu)先級(jí)(續(xù))

——例8_1350線程的優(yōu)先級(jí)(續(xù))

——例8_13運(yùn)行結(jié)果運(yùn)行結(jié)果Thread#1,tick=50000Thread#1,tick=100000Thread#1,tick=150000Thread#1,tick=200000Thread#1,tick=250000Thread#1,tick=300000Thread#1,tick=350000Thread#1,tick=400000Thread#0,tick=50000Thread#0,tick=100000Thread#0,tick=150000Thread#0,tick=200000Thread#0,tick=250000Thread#0,tick=300000Thread#0,tick=350000Thread#0,tick=400000結(jié)果說(shuō)明具有較高優(yōu)先級(jí)的線程1一直運(yùn)行到結(jié)束,具有較低優(yōu)先級(jí)的線程0才開(kāi)始運(yùn)行雖然具有較高優(yōu)先級(jí)的線程1調(diào)用了yield方法放棄CPU資源,允許線程0進(jìn)行爭(zhēng)奪,但馬上又被線程1搶奪了回去,所以有沒(méi)有yield方法都沒(méi)什么區(qū)別51如果在yield方法后增加一行sleep語(yǔ)句,讓線程1暫時(shí)放棄一下在CPU上的運(yùn)行,哪怕是1毫秒,那么線程0也可以有時(shí)機(jī)被調(diào)度。修改后的run方法如下publicvoidrun(){ while(tick<400000){tick++;if((tick%50000)==0){("Thread#"+num+",tick="+tick);yield();try{sleep(1);}catch(Exceptione){};}}} 線程的優(yōu)先級(jí)(續(xù))

——例8_13修改52線程的優(yōu)先級(jí)(續(xù))

——例8_13修改后運(yùn)行結(jié)果運(yùn)行結(jié)果Thread#1,tick=50000Thread#1,tick=100000Thread#1,tick=150000Thread#1,tick=200000Thread#0,tick=50000Thread#1,tick=250000Thread#1,tick=300000Thread#0,tick=100000Thread#1,tick=350000Thread#1,tick=400000Thread#0,tick=150000Thread#0,tick=200000Thread#0,tick=250000Thread#0,tick=300000Thread#0,tick=350000Thread#0,tick=400000說(shuō)明具有較低優(yōu)先權(quán)的線程0在線程1沒(méi)有執(zhí)行完畢前也獲得了一局部執(zhí)行,但線程1還是優(yōu)先完成了執(zhí)行通常,我們?cè)谝粋€(gè)線程內(nèi)部插入yield()語(yǔ)句,這個(gè)方法會(huì)使正在運(yùn)行的線程暫時(shí)放棄執(zhí)行,這是具有同樣優(yōu)先級(jí)的線程就有時(shí)機(jī)獲得調(diào)度開(kāi)始運(yùn)行,但較低優(yōu)先級(jí)的線程仍將被忽略不參加調(diào)度53本章小結(jié)本章內(nèi)容線程的根底知識(shí)線程的生命周期線程的優(yōu)先級(jí)本章要求了解線程的概念學(xué)會(huì)如何通過(guò)Thread類和Runnable接口創(chuàng)立線程,如何實(shí)現(xiàn)多線程的資源共享和通信,及如何控制線程的生命掌握線程同步的方法理解線程優(yōu)先級(jí)的概念,以及基于優(yōu)先級(jí)的線程調(diào)度54/holderholdingergersclassHoldInteger{ intshareInt=-1; booleanmoreData=true; publicvoidsetSharedInt(intval){shareInt=val;} publicintgetSharedInt(){returnshareInt;} publicvoidsetMoreData(booleanb){moreData=b;} publicbooleanhasMoreData(){returnmoreData;}}55//producerproducesintegersandputthemintoholderclassProducIntegerextendsThread{ HoldIntegerpHold; publicProducInteger(HoldIntegerh) { pHold=h; } publicvoidrun() { for(intcount=0;count<10;count++){

//sleepforrandominterval try{ Thread.sleep((int)(Math.random()*300)); } catch(InterruptedExceptione){ System.err.println(e.toString()); } pHold.setSharedInt(count); System.out.println("ProdecersetshareIntto"+count); }//setMoreDatatofalsewhencount<10.Itmeansholderisfull pHold.setMoreData(false); }}56//consumergetintegersfromholderclassConsumeIntegerextendsThread{ HoldIntegercHold; publicConsumeInteger(HoldIntegerh) { cHold=h; } publicvoidrun() { intval; while(cHold.hasMoreData()){ //sleepforrandominterval try{ Thread.sleep((int)(Math.random()*3000)); } catch(InterruptedExceptione){ System.err.println(e.toString());} val=cHold.getSharedInt();System.out.println("Consumerretrieved"+val);} }}57publicclasssynDemo{publicstaticvoidmain(Stringargs[]){HoldIntegerh=newHoldInteger();ProducIntegerp=newProducInteger(h);ConsumeIntegerc=newConsumeInteger(h);p.start();c.start();}}返回58運(yùn)行結(jié)果ProdecersetshareIntto0ProdecersetshareIntto1ProdecersetshareIntto2ProdecersetshareIntto3ProdecersetshareIntto4Consumerretrieved4ProdecersetshareIntto5ProdecersetshareIntto6ProdecersetshareIntto7ProdecersetshareIntto8ProdecersetshareIntto9Consumerretrieved9返回59掛起有時(shí)候兩個(gè)線程并不是同步的,即不涉及都需要調(diào)用一個(gè)同步方法,但線程也可能需要暫時(shí)的掛起。所謂掛起一個(gè)線程就是讓線程暫時(shí)讓出CPU的使用權(quán)限,暫時(shí)停止執(zhí)行,但停止執(zhí)行的持續(xù)時(shí)間不確定,因此不能使用sleep方法暫停線程。掛起一個(gè)線程需使用wait方法,即讓準(zhǔn)備掛起的線程調(diào)用wait方法,主動(dòng)讓出CPU的使用權(quán)限.恢復(fù)

為了恢復(fù)該線程,其它線程在占有CUP資源期間,讓掛起的線程的目標(biāo)對(duì)象執(zhí)行notifyAll()方法,使得掛起的線程繼續(xù)執(zhí)行;如果線程沒(méi)有目標(biāo)對(duì)象,為了恢復(fù)該線程,其它線程在占有CUP資源期間,讓掛起的線程調(diào)用notifyAll()方法,使掛起的線程繼續(xù)執(zhí)行。返回60Example9_13.java局部代碼publicsynchronizedvoid存取(intnumber)//存取方法{if(Thread.curre

溫馨提示

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

最新文檔

評(píng)論

0/150

提交評(píng)論