Java程序設(shè)計(jì)(鄭莉)-第8章_第1頁(yè)
Java程序設(shè)計(jì)(鄭莉)-第8章_第2頁(yè)
Java程序設(shè)計(jì)(鄭莉)-第8章_第3頁(yè)
Java程序設(shè)計(jì)(鄭莉)-第8章_第4頁(yè)
Java程序設(shè)計(jì)(鄭莉)-第8章_第5頁(yè)
已閱讀5頁(yè),還剩71頁(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)介

第8章線程鄭莉2目錄多線程編程根底線程的生命周期線程的優(yōu)先級(jí)本章小結(jié)38.1多線程編程根底本節(jié)內(nèi)容線程的概念Thread類Runnable接口線程間的數(shù)據(jù)共享多線程的同步控制線程之間的通信后臺(tái)線程48.1.1線程的概念

——多任務(wù)的實(shí)現(xiàn)多進(jìn)程一個(gè)獨(dú)立程序的每一次運(yùn)行稱為一個(gè)進(jìn)程,例如用字處理軟件編輯文稿時(shí),同時(shí)翻開mp3播放程序聽音樂(lè),這兩個(gè)獨(dú)立的程序在同時(shí)運(yùn)行,稱為兩個(gè)進(jìn)程進(jìn)程要占用相當(dāng)一局部處理器時(shí)間和內(nèi)存資源進(jìn)程具有獨(dú)立的內(nèi)存空間通信很不方便,編程模型比較復(fù)雜8.1多線程編程根底5多線程一個(gè)程序中多段代碼同時(shí)并發(fā)執(zhí)行,稱為多線程線程比進(jìn)程開銷小,協(xié)作和數(shù)據(jù)交換容易Java是第一個(gè)支持內(nèi)置線程操作的主流編程語(yǔ)言多數(shù)程序設(shè)計(jì)語(yǔ)言支持多線程要借助于操作系統(tǒng)“原語(yǔ)(primitives)”8.1多線程編程根底8.1.1線程的概念

——多任務(wù)的實(shí)現(xiàn)(續(xù))68.1.2Thread類Thread類直接繼承了Object類,并實(shí)現(xiàn)了Runnable接口。位于java.lang包中封裝了線程對(duì)象需要的屬性和方法繼承Thread類——?jiǎng)?chuàng)立多線程的方法之一從Thread類派生一個(gè)子類,并創(chuàng)立子類的對(duì)象子類應(yīng)該重寫Thread類的run方法,寫入需要在新線程中執(zhí)行的語(yǔ)句段。調(diào)用start方法來(lái)啟動(dòng)新線程,自動(dòng)進(jìn)入run方法。8.1多線程編程根底78.1.2Thread類(續(xù))

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

thread.start();//將自動(dòng)進(jìn)入run()方法System.out.println("mainthreadends");}}8.1多線程編程根底8classFactorialThreadextendsThread{privateintnum;publicFactorialThread(intnum){this.num=num;}publicvoidrun(){inti=num;ntresult=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");}}8.1.2Thread類(續(xù))

——例8-18.1多線程編程根底98.1.2Thread類(續(xù))

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

——例8-1修改:延長(zhǎng)主線程8.1多線程編程根底11修改后運(yùn)行結(jié)果mainthreadstartsnewthreadstaredThefactorialof10is3628800newthreadendsmainthreadends運(yùn)行結(jié)果說(shuō)明新線程結(jié)束后main線程才結(jié)束8.1.2Thread類(續(xù))

——例8-1修改后運(yùn)行結(jié)果8.1多線程編程根底128.1.2Thread類(續(xù))

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

——常用API函數(shù)8.1多線程編程根底14publicfinalvoidsetPriority(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)有死亡則返回true8.1.2Thread類(續(xù))

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

——例8-28.1多線程編程根底16classTestThread

extendsThread{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類(續(xù))

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

——例8-2運(yùn)行結(jié)果8.1多線程編程根底188.1.3Runnable接口Runnable接口Thread類實(shí)現(xiàn)了Runnable接口只有一個(gè)run()方法更便于多個(gè)線程共享資源Java不支持多繼承,如果已經(jīng)繼承了某個(gè)基類,便需要實(shí)現(xiàn)Runnable接口來(lái)生成多線程以實(shí)現(xiàn)runnable的對(duì)象為參數(shù)建立新的線程start方法啟動(dòng)線程就會(huì)運(yùn)行run()方法8.1多線程編程根底19使用Runnable接口實(shí)現(xiàn)例8-1功能publicclassFactorialThreadTester{publicstaticvoidmain(String[]args){System.out.println("mainthreadstarts");FactorialThreadt=newFactorialThread(10);//實(shí)現(xiàn)了Runnable的

newThread(t).start();//運(yùn)行FactorialThread的runSystem.out.println("newthreadstarted,mainthreadends");}}8.1.3Runnable接口(續(xù))

——例8-38.1多線程編程根底20classFactorialThreadimplementsRunnable{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接口(續(xù))

——例8-38.1多線程編程根底21使用Runnable接口實(shí)現(xiàn)例8-2功能publicclassThreadSleepTester{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");}}8.1.3Runnable接口(續(xù))

——例8-48.1多線程編程根底22classTestThreadimplementsRunnable{privateintsleepTime;publicTestThread()

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

}publicvoidrun(){try{System.out.println(

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

Thread.sleep(sleepTime);

}catch(InterruptedExceptionexception){};(Thread.currentThread().getName()+"finished");

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

——例8-48.1多線程編程根底238.1.4線程間的數(shù)據(jù)共享用同一個(gè)實(shí)現(xiàn)了Runnable接口的對(duì)象作為參數(shù)創(chuàng)立多個(gè)線程多個(gè)線程共享同一對(duì)象中的相同的數(shù)據(jù)8.1多線程編程根底24修改例8-4,只用一個(gè)Runnable類型的對(duì)象為參數(shù)創(chuàng)立3個(gè)新線程。publicclassShareTargetTester{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");}}8.1.4線程間的數(shù)據(jù)共享(續(xù))

——例8-58.1多線程編程根底25classTestThreadimplementsRunnable{ 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.1.4線程間的數(shù)據(jù)共享(續(xù))

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

——例8-5運(yùn)行結(jié)果8.1多線程編程根底278.1.4線程間的數(shù)據(jù)共享(續(xù))獨(dú)立的同時(shí)運(yùn)行的線程有時(shí)需要共享一些數(shù)據(jù)并且考慮到彼此的狀態(tài)和動(dòng)作8.1多線程編程根底28用三個(gè)線程模擬三個(gè)售票口,總共出售200張票用3個(gè)線程模仿3個(gè)售票口的售票行為這3個(gè)線程應(yīng)該共享200張票的數(shù)據(jù)publicclassSellTicketsTester{ publicstaticvoidmain(String[]args){ SellTicketst=newSellTickets();newThread(t).start();

newThread(t).start(); newThread(t).start(); }}8.1.4線程間的數(shù)據(jù)共享(續(xù))

——例8-68.1多線程編程根底29classSellTicketsimplementsRunnable{ privateinttickets=200;

publicvoidrun(){

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

——例8-68.1多線程編程根底30運(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張票,和原意就不符了8.1.4線程間的數(shù)據(jù)共享(續(xù))

——例8-6運(yùn)行結(jié)果8.1多線程編程根底318.1.5多線程的同步控制有時(shí)線程之間彼此不獨(dú)立、需要同步線程間的互斥同時(shí)運(yùn)行的幾個(gè)線程需要共享一個(gè)〔些〕數(shù)據(jù)共享的數(shù)據(jù),在某一時(shí)刻只允許一個(gè)線程對(duì)其進(jìn)行操作“生產(chǎn)者/消費(fèi)者”問(wèn)題假設(shè)有一個(gè)線程負(fù)責(zé)往數(shù)據(jù)區(qū)寫數(shù)據(jù),另一個(gè)線程從同一數(shù)據(jù)區(qū)中讀數(shù)據(jù),兩個(gè)線程可以并行執(zhí)行如果數(shù)據(jù)區(qū)已滿,生產(chǎn)者要等消費(fèi)者取走一些數(shù)據(jù)后才能再寫當(dāng)數(shù)據(jù)區(qū)空時(shí),消費(fèi)者要等生產(chǎn)者寫入一些數(shù)據(jù)后再取8.1多線程編程根底32用兩個(gè)線程模擬存票、售票過(guò)程假定開始售票處并沒(méi)有票,一個(gè)線程往里存票,另外一個(gè)線程那么往出賣票我們新建一個(gè)票類對(duì)象,讓存票和售票線程都訪問(wèn)它。本例采用兩個(gè)線程共享同一個(gè)數(shù)據(jù)對(duì)象來(lái)實(shí)現(xiàn)對(duì)同一份數(shù)據(jù)的操作publicclassProducerAndConsumer{ publicstaticvoidmain(String[]args){ Ticketst=newTickets(10);//建立票對(duì)象,票總數(shù)10 newConsumer(t).start();//開始賣票線程 newProducer(t).start();//開始存票線程 }}8.1.5多線程的同步控制(續(xù))

——例8-78.1多線程編程根底33classTickets{ intnumber=0;//票號(hào)

intsize;//總票數(shù)

booleanavailable=false;//表示目前是否有票可售

publicTickets(intsize)//構(gòu)造方法,傳入總票數(shù)參數(shù)

{ this.size=size; } }8.1.5多線程的同步控制(續(xù))

——例8-78.1多線程編程根底34classProducerextendsThread{ Ticketst=null; publicProducer(Ticketst)

{ this.t=t; } publicvoidrun() { while(t.number<t.size)

{ System.out.println("Producerputsticket"+(++t.number)); t.available=true;

} }}8.1.5多線程的同步控制(續(xù))

——例8-78.1多線程編程根底35classConsumerextendsThread//售票線程{ 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)//現(xiàn)有的票號(hào)賣完了

t.available=false; } }}8.1.5多線程的同步控制(續(xù))

——例8-78.1多線程編程根底36運(yùn)行結(jié)果Producerputsticket1Producerputsticket2Producerputsticket3Producerputsticket4Producerputsticket5Producerputsticket6Producerputsticket7Producerputsticket8Consumerbuysticket1Consumerbuysticket2Consumerbuysticket3Consumerbuysticket4Consumerbuysticket5Consumerbuysticket6Consumerbuysticket7Consumerbuysticket8Producerputsticket9Producerputsticket10Consumerbuysticket9Consumerbuysticket10.通過(guò)讓兩個(gè)線程操縱同一個(gè)票類對(duì)象,實(shí)現(xiàn)了數(shù)據(jù)共享的目的8.1.5多線程的同步控制(續(xù))

——例8-7運(yùn)行結(jié)果8.1多線程編程根底37如果我們?cè)趖.available=false之前加上sleep語(yǔ)句,讓售票線程多停留一會(huì),那么會(huì)出現(xiàn)錯(cuò)誤if(i==t.number){try{Thread.sleep(1);}catch(InterruptedExceptionexception){}; t.available=false;}假設(shè)售票線程運(yùn)行到t.available=false之前:CPU切換到存票線程,將available置為true,直到整個(gè)存票線程結(jié)束。再次切換到售票線程:執(zhí)行t.available=false。此時(shí)售票號(hào)小于存票數(shù),且存票線程已經(jīng)結(jié)束不再能將t.available置為true,那么售票線程陷入了死循環(huán)8.1.5多線程的同步控制(續(xù))

——例8-7可能出現(xiàn)的錯(cuò)誤8.1多線程編程根底38修改后運(yùn)行結(jié)果Producerputsticket1Producerputsticket2Producerputsticket3Producerputsticket4Producerputsticket5Producerputsticket6Producerputsticket7Producerputsticket8Consumerbuysticket1Consumerbuysticket2Consumerbuysticket3Consumerbuysticket4Consumerbuysticket5Consumerbuysticket6Consumerbuysticket7Consumerbuysticket8Producerputsticket9Producerputsticket108.1.5多線程的同步控制(續(xù))

——例8-7修改后運(yùn)行結(jié)果8.1多線程編程根底39線程同步互斥:許多線程在同一個(gè)共享數(shù)據(jù)上操作而互不干擾,同一時(shí)刻只能有一個(gè)線程訪問(wèn)該共享數(shù)據(jù)。因此有些方法或程序段在同一時(shí)刻只能被一個(gè)線程執(zhí)行,稱之為監(jiān)視區(qū)協(xié)作:多個(gè)線程可以有條件地同時(shí)操作共享數(shù)據(jù)。執(zhí)行監(jiān)視區(qū)代碼的線程在條件滿足的情況下可以允許其它線程進(jìn)入監(jiān)視區(qū)8.1.5多線程的同步控制(續(xù))

——線程同步(Synchronization)8.1多線程編程根底40synchronized——線程同步關(guān)鍵字,實(shí)現(xiàn)互斥用于指定需要同步的代碼段或方法,也就是監(jiān)視區(qū)可實(shí)現(xiàn)與一個(gè)鎖旗標(biāo)的交互。例如:synchronized〔對(duì)象〕{代碼段}synchronized的功能是:首先判斷對(duì)象的鎖旗標(biāo)是否在,如果在就獲得鎖旗標(biāo),然后就可以執(zhí)行緊隨其后的代碼段;如果對(duì)象的鎖旗標(biāo)不在〔已被其他線程拿走〕,就進(jìn)入等待狀態(tài),直到獲得鎖旗標(biāo)當(dāng)被synchronized限定的代碼段執(zhí)行完,就釋放鎖旗標(biāo)8.1.5多線程的同步控制(續(xù))

——synchronized關(guān)鍵字8.1多線程編程根底41互斥存票線程和售票線程應(yīng)保持互斥關(guān)系。即售票線程執(zhí)行時(shí)不進(jìn)入存票線程、存票線程執(zhí)行時(shí)不進(jìn)入售票線程Java使用監(jiān)視器機(jī)制每個(gè)對(duì)象只有一個(gè)“鎖旗標(biāo)”,利用多線程對(duì)“鎖旗標(biāo)”的爭(zhēng)奪實(shí)現(xiàn)線程間的互斥當(dāng)線程A獲得了一個(gè)對(duì)象的鎖旗標(biāo)后,線程B必須等待線程A完成規(guī)定的操作、并釋放出鎖旗標(biāo)后,才能獲得該對(duì)象的鎖旗標(biāo),并執(zhí)行線程B中的操作8.1.5多線程的同步控制(續(xù))

——解決例8-7的問(wèn)題8.1多線程編程根底42將需要互斥的語(yǔ)句段放入synchronized(object){}語(yǔ)句中,且兩處的object是相同的classProducerextendsThread{ Ticketst=null; publicProducer(Ticketst){this.t=t;} publicvoidrun(){ while((t.number)<t.size){

synchronized(t)

{//申請(qǐng)對(duì)象t的鎖旗標(biāo) System.out.println("Producerputsticket"+(++t.number)); t.available=true; }//釋放對(duì)象t的鎖旗標(biāo)

} System.out.println("Producerends!"); }}8.1.5多線程的同步控制(續(xù))

——例8-88.1多線程編程根底43classConsumerextendsThread{ Ticketst=null; inti=0; publicConsumer(Ticketst){this.t=t;} publicvoidrun(){ while(i<t.size){

synchronized(t){//申請(qǐng)對(duì)象t的鎖旗標(biāo)

if(t.available==true&&i<=t.number) System.out.println("Consumerbuysticket"+(++i)); if(i==t.number){try{Thread.sleep(1);}catch(Exceptione){} t.available=false; } } //釋放對(duì)象t的鎖旗標(biāo)

} System.out.println("Consumerends"); }}8.1.5多線程的同步控制(續(xù))

——例8-88.1多線程編程根底44說(shuō)明存票程序段和售票程序段為獲得同一對(duì)象的鎖旗標(biāo)而實(shí)現(xiàn)互斥操作當(dāng)線程執(zhí)行到synchronized的時(shí)候,檢查傳入的實(shí)參對(duì)象,并申請(qǐng)得到該對(duì)象的鎖旗標(biāo)。如果得不到,那么線程就被放到一個(gè)與該對(duì)象鎖旗標(biāo)相對(duì)應(yīng)的等待線程池中。直到該對(duì)象的鎖旗標(biāo)被歸還,池中的等待線程才能重新去獲得鎖旗標(biāo),然后繼續(xù)執(zhí)行下去除了可以對(duì)指定的代碼段進(jìn)行同步控制之外,還可以定義整個(gè)方法在同步控制下執(zhí)行,只要在方法定義前加上synchronized關(guān)鍵字即可8.1.5多線程的同步控制(續(xù))

——synchronized關(guān)鍵字8.1多線程編程根底45實(shí)現(xiàn)例8-7功能。將互斥方法放在共享的資源類Tickets中classTickets{ intsize;//票總數(shù)

intnumber=0;//存票序號(hào)

inti=0;//售票序號(hào)

booleanavailable=false;//是否有待售的票

publicTickets(intsize){this.size=size;}

publicsynchronizedvoidput()

{//同步方法,實(shí)現(xiàn)存票的功能

System.out.println("Producerputsticket"+(++number)); available=true; }

publicsynchronizedvoidsell()

{//同步方法,實(shí)現(xiàn)售票的功能

if(available==true&&i<=number) System.out.println("Consumerbuysticket"+(++i)); if(i==number)available=false; } }8.1.5多線程的同步控制(續(xù))

——例8-98.1多線程編程根底468.1.5多線程的同步控制(續(xù))

——例8-9classProducerextendsThread{ Ticketst=null; publicProducer(Ticketst) { this.t=t; } publicvoidrun() {//如果存票數(shù)小于限定總量,那么不斷存入票 while(t.number<t.size) t.put(); } }8.1多線程編程根底478.1.5多線程的同步控制(續(xù))

——例8-9classConsumerextendsThread{ Ticketst=null; publicConsumer(Ticketst) { this.t=t;} publicvoidrun() {//如果售票數(shù)小于限定總量,那么不斷售票 while(t.i<t.size) t.sell(); }}8.1多線程編程根底48為了更有效地協(xié)調(diào)不同線程的工作,需要在線程間建立溝通渠道,通過(guò)線程間的“對(duì)話”來(lái)解決線程間的同步問(wèn)題java.lang.Object類的一些方法為線程間的通訊提供了有效手段wait()如果當(dāng)前狀態(tài)不適合本線程執(zhí)行,正在執(zhí)行同步代碼〔synchronized〕的某個(gè)線程A調(diào)用該方法〔在對(duì)象x上〕,該線程暫停執(zhí)行而進(jìn)入對(duì)象x的等待池,并釋放已獲得的對(duì)象x的鎖旗標(biāo)。線程A要一直等到其他線程在對(duì)象x上調(diào)用notify或notifyAll方法,才能夠在重新獲得對(duì)象x的鎖旗標(biāo)后繼續(xù)執(zhí)行〔從wait語(yǔ)句后繼續(xù)執(zhí)行〕8.1多線程編程根底8.1.6線程之間的通信49notify()隨機(jī)喚醒一個(gè)等待的線程,本線程繼續(xù)執(zhí)行線程被喚醒以后,還要等發(fā)出喚醒消息者釋放監(jiān)視器,這期間關(guān)鍵數(shù)據(jù)仍可能被改變被喚醒的線程開始執(zhí)行時(shí),一定要判斷當(dāng)前狀態(tài)是否適合自己運(yùn)行notifyAll()喚醒所有等待的線程,本線程繼續(xù)執(zhí)行8.1多線程編程根底8.1.6線程之間的通信(續(xù))

——notify()和notifyAll()方法50修改例8-9,使每存入一張票,就售一張票,售出后,再存入classTickets{ ……publicsynchronizedvoidput(){ if(available)//如果還有存票待售,那么存票線程等待 try{wait();}catch(Exceptione){} System.out.println("Producerputsticket"+(++number)); available=true; notify();//存票后喚醒售票線程開始售票 } publicsynchronizedvoidsell(){ if(!available)//如果沒(méi)有存票,那么售票線程等待 try{wait();}catch(Exceptione){} System.out.println("Consumerbuysticket"+(number)); available=false; notify(); //售票后喚醒存票線程開始存票if(number==size)number=size+1;//在售完最后一張票后,//設(shè)置一個(gè)結(jié)束標(biāo)志,number>size表示售票結(jié)束 } }8.1多線程編程根底8.1.6線程之間的通信(續(xù))

——例8-1051classProducerextendsThread{ Ticketst=null; publicProducer(Ticketst){this.t=t;} publicvoidrun(){ while(t.number<t.size)t.put(); } }classConsumerextendsThread{ Ticketst=null; publicConsumer(Ticketst){this.t=t;} publicvoidrun(){ while(t.number<=t.size)t.sell(); } }8.1多線程編程根底8.1.6線程之間的通信(續(xù))

——例8-1052運(yùn)行結(jié)果Producerputsticket1Consumerbuysticket1Producerputsticket2Consumerbuysticket2Producerputsticket3Consumerbuysticket3Producerputsticket4Consumerbuysticket4Producerputsticket5Consumerbuysticket5Producerputsticket6Consumerbuysticket6Producerputsticket7Consumerbuysticket7Producerputsticket8Consumerbuysticket8Producerputsticket9Consumerbuysticket9Producerputsticket10Consumerbuysticket108.1多線程編程根底8.1.6線程之間的通信(續(xù))

——例8-10運(yùn)行結(jié)果53程序說(shuō)明當(dāng)Consumer線程售出票后,available值變?yōu)閒alse,當(dāng)Producer線程放入票后,available值變?yōu)閠rue只有available為true時(shí),Consumer線程才能售票,否那么就必須等待Producer線程放入新的票后的通知只有available為false時(shí),Producer線程才能放票,否那么必須等待Consumer線程售出票后的通知8.1多線程編程根底8.1.6線程之間的通信(續(xù))

——例8-10說(shuō)明548.1.7后臺(tái)線程后臺(tái)線程也叫守護(hù)線程,通常是為了輔助其它線程而運(yùn)行的線程它不阻礙程序終止一個(gè)進(jìn)程中只要還有一個(gè)前臺(tái)線程在運(yùn)行,這個(gè)進(jìn)程就不會(huì)結(jié)束;如果一個(gè)進(jìn)程中的所有前臺(tái)線程都已經(jīng)結(jié)束,那么無(wú)論是否還有未結(jié)束的后臺(tái)線程,這個(gè)進(jìn)程都會(huì)結(jié)束“垃圾回收”便是一個(gè)后臺(tái)線程如果對(duì)某個(gè)線程對(duì)象在啟動(dòng)〔調(diào)用start方法〕之前調(diào)用了setDaemon(true)方法,這個(gè)線程就變成了后臺(tái)線程8.1多線程編程根底55創(chuàng)立一個(gè)無(wú)限循環(huán)的后臺(tái)線程,驗(yàn)證主線程結(jié)束后,程序即結(jié)束publicclassEx8_10{ publicstaticvoidmain(String[]args){ ThreadTestt=newThreadTest(); t.setDaemon(true); t.start(); }}classThreadTestextendsThread{ publicvoidrun(){ while(true){}}}運(yùn)行程序,那么發(fā)現(xiàn)整個(gè)程序在主線程結(jié)束時(shí)就隨之中止運(yùn)行了,如果注釋掉t.setDaemon(true)語(yǔ)句,那么程序永遠(yuǎn)不會(huì)結(jié)束8.1多線程編程根底8.1.7后臺(tái)線程(續(xù))

——例8-11568.2線程的生命周期線程的生命周期線程從產(chǎn)生到消亡的過(guò)程一個(gè)線程在任何時(shí)刻都處于某種線程狀態(tài)〔threadstate〕578.2.1線程的幾種根本狀態(tài)線程生命周期狀態(tài)圖8.2線程的生命周期58誕生狀態(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)線程已完成或退出8.2.1線程的幾種根本狀態(tài)(續(xù))8.2線程的生命周期598.2.2死鎖問(wèn)題死鎖線程在運(yùn)行過(guò)程中,其中某個(gè)步驟往往需要滿足一些條件才能繼續(xù)進(jìn)行下去,如果這個(gè)條件不能滿足,線程將在這個(gè)步驟上出現(xiàn)阻塞線程A可能會(huì)陷于對(duì)線程B的等待,而線程B同樣陷于對(duì)線程C的等待,依次類推,整個(gè)等待鏈最后又可能回到線程A。如此一來(lái)便陷入一個(gè)彼此等待的輪回中,任何線程都動(dòng)彈不得,此即所謂死鎖〔deadlock〕對(duì)于死鎖問(wèn)題,關(guān)鍵不在于出現(xiàn)問(wèn)題后調(diào)試,而是在于預(yù)防8.2線程的生命周期60設(shè)想一個(gè)游戲,規(guī)那么為3個(gè)人站在三角形的三個(gè)頂點(diǎn)的位置上,三個(gè)邊上放著三個(gè)球,如下圖。每個(gè)人都必須先拿到自己左手邊的球,才能再拿到右手邊的球,兩手都有球之后,才能夠把兩個(gè)球都放下Player_0Player_1Player_20218.2線程的生命周期8.2.2死鎖問(wèn)題(續(xù))

——例8-1261例8_11創(chuàng)立3個(gè)線程模擬3個(gè)游戲者的行為。publicclassEx8_11{ publicstaticvoidmain(String[]args){ Ballsball=newBalls(); //新建一個(gè)球類對(duì)象 Player0p0=newPlayer0(ball);//創(chuàng)立0號(hào)游戲者 Player1p1=newPlayer1(ball);//創(chuàng)立1號(hào)游戲者 Player2p2=newPlayer2(ball);//創(chuàng)立2號(hào)游戲者 p0.start();//啟動(dòng)0號(hào)游戲者 p1.start();//啟動(dòng)1號(hào)游戲者 p2.start();//啟動(dòng)2號(hào)游戲者 }}classBalls{//球類 booleanflag0=false;//0號(hào)球的標(biāo)志變量,true表示已被人拿,false表示未被任何人拿 booleanflag1=false;//1號(hào)球的標(biāo)志變量 booleanflag2=false;//2號(hào)球的標(biāo)志變量}8.2線程的生命周期8.2.2死鎖問(wèn)題(續(xù))

——例8-1262classPlayer0extendsThread{//0號(hào)游戲者的類 privateBallsball; publicPlayer0(Ballsb) {this.ball=b;} publicvoidrun(){ while(true){ while(ball.flag1==true){};//如果1號(hào)球已被拿走,那么等待 ball.flag1=true;//拿起1號(hào)球 while(ball.flag0==true){};//如果0號(hào)球已被拿走,那么等待 if(ball.flag1==true&&ball.flag0==false){ ball.flag0=true;//拿起0號(hào)球 System.out.println("Player0hasgottwoballs!"); ball.flag1=false;//放下1號(hào)球 ball.flag0=false;//放下0號(hào)球 try{sleep(1);}catch(Exceptione){};//放下后休息1ms }} }}8.2線程的生命周期8.2.2死鎖問(wèn)題(續(xù))

——例8-1263classPlayer1extendsThread//1號(hào)游戲者的類{ privateBallsball; publicPlayer1(Ballsb){this.ball=b;} publicvoidrun() {while(true) {while(ball.flag0==true){}; ball.flag0=true; while(ball.flag2==true){}; if(ball.flag0==true&&ball.flag2==false) {ball.flag2=true; System.out.println("Player1hasgottwoballs!"); ball.flag0=false; ball.flag2=false; try{sleep(1);}catch(Exceptione){}; } } }}8.2線程的生命周期8.2.2死鎖問(wèn)題(續(xù))

——例8-1264classPlayer2extendsThread//2號(hào)游戲者的類{ privateBallsball; publicPlayer2(Ballsb){this.ball=b;} publicvoidrun() {while(true) { while(ball.flag2==true){}; ball.flag2=true; while(ball.flag1==true){}; if(ball.flag2==true&&ball.flag1==false) {ball.flag1=true; System.out.println("Player2hasgottwoballs!");

ball.flag1=false; ball.flag2=false;

try{sleep(1);}catch(Exceptione){}; } } }}8.2線程的生命周期8.2.2死鎖問(wèn)題(續(xù))

——例8-1265運(yùn)行結(jié)果假設(shè)干次后將陷入死鎖,不再有輸出信息,即任何人都不能再同時(shí)擁有兩側(cè)的球程序說(shuō)明如果剛好3個(gè)人都拿到了左手邊的球,都等待那右手邊的球,那么因?yàn)檎l(shuí)都不能放手,那么這3個(gè)線程都將陷入無(wú)止盡的等待當(dāng)中,這就構(gòu)成了死鎖為了便于觀察死鎖發(fā)生的條件,我們?cè)诿總€(gè)游戲者放下兩邊的球后增加了sleep語(yǔ)句為了防止死鎖,需要修改游戲規(guī)那么,使每個(gè)人都只能先搶到兩側(cè)中號(hào)比較小的球,才能拿另一只球,這樣就不會(huì)再出現(xiàn)死鎖現(xiàn)象8.2線程的生命周期8.2.2死鎖問(wèn)題(續(xù))

——例8-12運(yùn)行結(jié)果668.2.3控制線程的生命結(jié)束線程的生命用stop方法可以結(jié)束線程的生命但如果一個(gè)線程正在操作共享數(shù)據(jù)段,操作過(guò)程沒(méi)有完成就用stop結(jié)束的話,將會(huì)導(dǎo)致數(shù)據(jù)的不完整,因此并不提倡使用此方法通常,可通過(guò)控制run方法中循環(huán)條件的方式來(lái)結(jié)束一個(gè)線程8.2線程的生命周期67線程不斷顯示遞增整數(shù),按下回車鍵那么停止執(zhí)行importjava.io.*;publicclassEx8_12{ publicstaticvoidmain(String[]args)throwsIOException{ TestThreadt=newTestThread(); t.start(); newBufferedReader(newInputStreamReader(System.in)).readLine();//等待鍵盤輸入 t.stopme();//調(diào)用stopme方法結(jié)束t線程 }}8.2線程的生命周期8.2.2控制線程的生命(續(xù))

——例8-1368classTestThreadextendsThread{ privatebooleanflag=true; publicvoidstopme(){//在此方法中控制循環(huán)條件 flag=false; } publicvoidrun(){ inti=0; while(flag){ System.out.println(i++);//如果flag為真那么一直顯示遞增整數(shù) } }}運(yùn)行效果為按下回車鍵后那么停止顯示8.2線程的生命周期8.2.2控制線程的生命(續(xù))

——例8-13698.3線程的優(yōu)先級(jí)線程調(diào)度在單CPU的系統(tǒng)中,多個(gè)線程需要共享CPU,在任何時(shí)間點(diǎn)上實(shí)際只能有一個(gè)線程在運(yùn)行控制多個(gè)線程在同一個(gè)CPU上以某種順序運(yùn)行稱為線程調(diào)度Java虛擬機(jī)支持一種非常簡(jiǎn)單的、確定的調(diào)度算法,叫做固定優(yōu)先級(jí)算法。這個(gè)算法基于線程的優(yōu)先級(jí)對(duì)其進(jìn)行調(diào)度70線程的優(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í)8.3線程的優(yōu)先級(jí)(續(xù))71基于線程優(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)保證算法的正確性8.3線程的優(yōu)先級(jí)(續(xù))72假設(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í)間期

溫馨提示

  • 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)論