Java程序設計(鄭莉)-第8章_第1頁
Java程序設計(鄭莉)-第8章_第2頁
Java程序設計(鄭莉)-第8章_第3頁
Java程序設計(鄭莉)-第8章_第4頁
Java程序設計(鄭莉)-第8章_第5頁
已閱讀5頁,還剩71頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

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

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

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

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

thread.start();//將自動進入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運行結(jié)果運行結(jié)果mainthreadstartsmainthreadendsnewthreadstartedThefactorialof10is3628800newthreadends結(jié)果說明main線程已經(jīng)執(zhí)行完后,新線程才執(zhí)行完main函數(shù)調(diào)用thread.start()方法啟動新線程后并不等待其run方法返回就繼續(xù)運行,thread.run函數(shù)在一邊單獨運行,不影響原來的main函數(shù)的運行8.1多線程編程根底10源程序修改如果啟動新線程后希望主線程多持續(xù)一會再結(jié)束,可在start語句后加上讓當前線程〔這里當然是main〕休息1毫秒的語句:try{Thread.sleep(1);}catch(Exceptione){};8.1.2Thread類(續(xù))

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

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

——常用API方法名稱說明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)造一個新的線程對象,并同時指定線程名publicstaticThreadcurrentThread()返回當前正在運行的線程對象publicstaticvoidyield()使當前線程對象暫停,允許別的線程開始運行publicstaticvoidsleep(longmillis)使當前線程暫停運行指定毫秒數(shù),但此線程并不失去已獲得的鎖旗標。8.1多線程編程根底13publicvoidstart()啟動線程,JVM將調(diào)用此線程的run方法,結(jié)果是將同時運行兩個線程,當前線程和執(zhí)行run方法的線程publicvoidrun()Thread的子類應該重寫此方法,內(nèi)容應為該線程應執(zhí)行的任務。publicfinalvoidstop()停止線程運行,釋放該線程占用的對象鎖旗標。publicvoidinterrupt()中斷此線程publicfinalvoidjoin()如果此前啟動了線程A,調(diào)用join方法將等待線程A死亡才能繼續(xù)執(zhí)行當前線程publicfinalvoidjoin(longmillis)如果此前啟動了線程A,調(diào)用join方法將等待指定毫秒數(shù)或線程A死亡才能繼續(xù)執(zhí)行當前線程8.1.2Thread類(續(xù))

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

——常用API函數(shù)8.1多線程編程根底15創(chuàng)立3個新線程,每個線程睡眠一段時間〔0~6秒〕,然后結(jié)束publicclassThreadSleepTester{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");}}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運行結(jié)果StartingthreadsThreadsstarted,mainendsthread1goingtosleepfor3519thread2goingtosleepfor1689thread3goingtosleepfor5565thread2finishedthread1finishedthread3finished說明由于線程3休眠時間最長,所以最后結(jié)束,線程2休眠時間最短,所以最先結(jié)束每次運行,都會產(chǎn)生不同的隨機休眠時間,所以結(jié)果都不相同8.1.2Thread類(續(xù))

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

newThread(t).start();//運行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接口實現(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ù)共享用同一個實現(xiàn)了Runnable接口的對象作為參數(shù)創(chuàng)立多個線程多個線程共享同一對象中的相同的數(shù)據(jù)8.1多線程編程根底24修改例8-4,只用一個Runnable類型的對象為參數(shù)創(chuàng)立3個新線程。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運行結(jié)果StartingthreadsThread1goingtosleepfor966Thread2goingtosleepfor966Threadsstarted,mainendsThread3goingtosleepfor966Thread1finishedThread2finishedThread3finished說明因為是用一個Runnable類型對象創(chuàng)立的3個新線程,這三個線程就共享了這個對象的私有成員sleepTime,在本次運行中,三個線程都休眠了966毫秒8.1.4線程間的數(shù)據(jù)共享(續(xù))

——例8-5運行結(jié)果8.1多線程編程根底278.1.4線程間的數(shù)據(jù)共享(續(xù))獨立的同時運行的線程有時需要共享一些數(shù)據(jù)并且考慮到彼此的狀態(tài)和動作8.1多線程編程根底28用三個線程模擬三個售票口,總共出售200張票用3個線程模仿3個售票口的售票行為這3個線程應該共享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運行結(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.1.4線程間的數(shù)據(jù)共享(續(xù))

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

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

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)有的票號賣完了

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

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

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

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

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

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

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

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

synchronized(t)

{//申請對象t的鎖旗標 System.out.println("Producerputsticket"+(++t.number)); t.available=true; }//釋放對象t的鎖旗標

} 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){//申請對象t的鎖旗標

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; } } //釋放對象t的鎖旗標

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

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

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

intnumber=0;//存票序號

inti=0;//售票序號

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

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

publicsynchronizedvoidput()

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

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

publicsynchronizedvoidsell()

{//同步方法,實現(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)不同線程的工作,需要在線程間建立溝通渠道,通過線程間的“對話”來解決線程間的同步問題java.lang.Object類的一些方法為線程間的通訊提供了有效手段wait()如果當前狀態(tài)不適合本線程執(zhí)行,正在執(zhí)行同步代碼〔synchronized〕的某個線程A調(diào)用該方法〔在對象x上〕,該線程暫停執(zhí)行而進入對象x的等待池,并釋放已獲得的對象x的鎖旗標。線程A要一直等到其他線程在對象x上調(diào)用notify或notifyAll方法,才能夠在重新獲得對象x的鎖旗標后繼續(xù)執(zhí)行〔從wait語句后繼續(xù)執(zhí)行〕8.1多線程編程根底8.1.6線程之間的通信49notify()隨機喚醒一個等待的線程,本線程繼續(xù)執(zhí)行線程被喚醒以后,還要等發(fā)出喚醒消息者釋放監(jiān)視器,這期間關鍵數(shù)據(jù)仍可能被改變被喚醒的線程開始執(zhí)行時,一定要判斷當前狀態(tài)是否適合自己運行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)//如果沒有存票,那么售票線程等待 try{wait();}catch(Exceptione){} System.out.println("Consumerbuysticket"+(number)); available=false; notify(); //售票后喚醒存票線程開始存票if(number==size)number=size+1;//在售完最后一張票后,//設置一個結(jié)束標志,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運行結(jié)果Producerputsticket1Consumerbuysticket1Producerputsticket2Consumerbuysticket2Producerputsticket3Consumerbuysticket3Producerputsticket4Consumerbuysticket4Producerputsticket5Consumerbuysticket5Producerputsticket6Consumerbuysticket6Producerputsticket7Consumerbuysticket7Producerputsticket8Consumerbuysticket8Producerputsticket9Consumerbuysticket9Producerputsticket10Consumerbuysticket108.1多線程編程根底8.1.6線程之間的通信(續(xù))

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

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

——例8-11568.2線程的生命周期線程的生命周期線程從產(chǎn)生到消亡的過程一個線程在任何時刻都處于某種線程狀態(tài)〔threadstate〕578.2.1線程的幾種根本狀態(tài)線程生命周期狀態(tài)圖8.2線程的生命周期58誕生狀態(tài)線程剛剛被創(chuàng)立就緒狀態(tài)線程的start方法已被執(zhí)行線程已準備好運行運行狀態(tài)處理機分配給了線程,線程正在運行阻塞狀態(tài)〔Blocked〕在線程發(fā)出輸入/輸出請求且必須等待其返回遇到用synchronized標記的方法而未獲得其監(jiān)視器暫時不能進入執(zhí)行時休眠狀態(tài)〔Sleeping〕執(zhí)行sleep方法而進入休眠死亡狀態(tài)線程已完成或退出8.2.1線程的幾種根本狀態(tài)(續(xù))8.2線程的生命周期598.2.2死鎖問題死鎖線程在運行過程中,其中某個步驟往往需要滿足一些條件才能繼續(xù)進行下去,如果這個條件不能滿足,線程將在這個步驟上出現(xiàn)阻塞線程A可能會陷于對線程B的等待,而線程B同樣陷于對線程C的等待,依次類推,整個等待鏈最后又可能回到線程A。如此一來便陷入一個彼此等待的輪回中,任何線程都動彈不得,此即所謂死鎖〔deadlock〕對于死鎖問題,關鍵不在于出現(xiàn)問題后調(diào)試,而是在于預防8.2線程的生命周期60設想一個游戲,規(guī)那么為3個人站在三角形的三個頂點的位置上,三個邊上放著三個球,如下圖。每個人都必須先拿到自己左手邊的球,才能再拿到右手邊的球,兩手都有球之后,才能夠把兩個球都放下Player_0Player_1Player_20218.2線程的生命周期8.2.2死鎖問題(續(xù))

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

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

——例8-1263classPlayer1extendsThread//1號游戲者的類{ 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死鎖問題(續(xù))

——例8-1264classPlayer2extendsThread//2號游戲者的類{ 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死鎖問題(續(xù))

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

——例8-12運行結(jié)果668.2.3控制線程的生命結(jié)束線程的生命用stop方法可以結(jié)束線程的生命但如果一個線程正在操作共享數(shù)據(jù)段,操作過程沒有完成就用stop結(jié)束的話,將會導致數(shù)據(jù)的不完整,因此并不提倡使用此方法通常,可通過控制run方法中循環(huán)條件的方式來結(jié)束一個線程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ù) } }}運行效果為按下回車鍵后那么停止顯示8.2線程的生命周期8.2.2控制線程的生命(續(xù))

——例8-13698.3線程的優(yōu)先級線程調(diào)度在單CPU的系統(tǒng)中,多個線程需要共享CPU,在任何時間點上實際只能有一個線程在運行控制多個線程在同一個CPU上以某種順序運行稱為線程調(diào)度Java虛擬機支持一種非常簡單的、確定的調(diào)度算法,叫做固定優(yōu)先級算法。這個算法基于線程的優(yōu)先級對其進行調(diào)度70線程的優(yōu)先級每個Java線程都有一個優(yōu)先級,其范圍都在1和10之間。默認情況下,每個線程的優(yōu)先級都設置為5在線程A運行過程中創(chuàng)立的新的線程對象B,初始狀態(tài)具有和線程A相同的優(yōu)先級如果A是個后臺線程,那么B也是個后臺線程可在線程創(chuàng)立之后的任何時候,通過setPriority(intpriority)方法改變其原來的優(yōu)先級8.3線程的優(yōu)先級(續(xù))71基于線程優(yōu)先級的線程調(diào)度具有較高優(yōu)先級的線程比優(yōu)先級較低的線程優(yōu)先執(zhí)行對具有相同優(yōu)先級的線程,Java的處理是隨機的底層操作系統(tǒng)支持的優(yōu)先級可能要少于10個,這樣會造成一些混亂。因此,只能將優(yōu)先級作為一種很粗略的工具使用。最后的控制可以通過明智地使用yield()函數(shù)來完成我們只能基于效率的考慮來使用線程優(yōu)先級,而不能依靠線程優(yōu)先級來保證算法的正確性8.3線程的優(yōu)先級(續(xù))72假設某線程正在運行,那么只有出現(xiàn)以下情況之一,才會使其暫停運行一個具有更高優(yōu)先級的線程變?yōu)榫途w狀態(tài)〔Ready〕;由于輸入/輸出〔或其他一些原因〕、調(diào)用sleep、wait、yield方法使其發(fā)生阻塞;對于支持時間分片的系統(tǒ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. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論