




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
第八章線程JAVA語言程序設(shè)計1目錄多線程編程基礎(chǔ)線程的生命周期線程的優(yōu)先級本章小結(jié)28.1多線程編程基礎(chǔ)本節(jié)內(nèi)容線程的概念Thread類Runnable接口線程間的數(shù)據(jù)共享多線程的同步控制線程之間的通信后臺線程38.1.1線程的概念進程和線程的區(qū)別進程一個獨立程序的每一次運行稱為一個進程,例如用字處理軟件編輯文稿時,同時打開mp3播放程序聽音樂,這兩個獨立的程序在同時運行,稱為兩個進程設(shè)置一個進程要占用相當一部分處理器時間和內(nèi)存資源大多數(shù)操作系統(tǒng)不允許進程訪問其他進程的內(nèi)存空間,進程間的通信很不方便,編程模型比較復(fù)雜
多線程編程基礎(chǔ)4線程一個程序中多段代碼同時并發(fā)執(zhí)行,稱為多線程通過多線程,一個進程表面上看同時可以執(zhí)行一個以上的任務(wù)——并發(fā)創(chuàng)建線程比創(chuàng)建進程開銷要小得多,線程之間的協(xié)作和數(shù)據(jù)交換也比較容易Java是第一個支持內(nèi)置線程操作的主流編程語言多數(shù)程序設(shè)計語言支持多線程要借助于操作系統(tǒng)“原語(primitives)”8.1.1線程的概念(續(xù))多線程編程基礎(chǔ)58.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任何包就可直接使用多線程編程基礎(chǔ)68.1.2Thread類(續(xù))
——例8_1在新線程中完成計算某個整數(shù)的階乘publicclassEx8_1{publicstaticvoidmain(String[]args){
System.out.println("mainthreadstarts");
FactorialThreadthread=newFactorialThread(10);
thread.start();
System.out.println("mainthreadends");}}classFactorialThread
extends
Thread{privateintnum;publicFactorialThread(intnum){this.num=num;
}
多線程編程基礎(chǔ)7publicvoidrun(){
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é)果mainthreadstartsmainthreadendsnewthreadstartedThefactorialof10is3628800newthreadends8.1.2Thread類(續(xù))
——例8_1運行結(jié)果多線程編程基礎(chǔ)8結(jié)果說明main線程已經(jīng)執(zhí)行完后,新線程才執(zhí)行完main函數(shù)調(diào)用thread.start()方法啟動新線程后并不等待其run方法返回就繼續(xù)運行,thread.run函數(shù)在一邊獨自運行,不影響原來的main函數(shù)的運行源程序修改如果啟動新線程后希望主線程多持續(xù)一會再結(jié)束,可在start語句后加上讓當前線程(這里當然是main)休息1毫秒的語句:try{Thread.sleep(1);}catch(Exceptione){};8.1.2Thread類(續(xù))
——例8_1修改多線程編程基礎(chǔ)9修改后運行結(jié)果mainthreadstartsnewthreadstaredThefactorialof10is3628800newthreadendsmainthreadends運行結(jié)果說明新線程結(jié)束后main線程才結(jié)束8.1.2Thread類(續(xù))
——例8_1修改后運行結(jié)果多線程編程基礎(chǔ)108.1.2Thread類(續(xù))
——常用API函數(shù)名稱說明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(long
millis)使當前線程暫停運行指定毫秒數(shù),但此線程并不失去已獲得的鎖旗標。多線程編程基礎(chǔ)11publicvoidstart()啟動線程,JVM將調(diào)用此線程的run方法,結(jié)果是將同時運行兩個線程,當前線程和執(zhí)行run方法的線程publicvoidrun()Thread的子類應(yīng)該重寫此方法,內(nèi)容應(yīng)為該線程應(yīng)執(zhí)行的任務(wù)。publicfinalvoidstop()停止線程運行,釋放該線程占用的對象鎖旗標。publicvoidinterrupt()打斷此線程publicfinalvoidjoin()在當前線程中加入調(diào)用join方法的線程A,直到線程A死亡才能繼續(xù)執(zhí)行當前線程publicfinalvoidjoin(long
millis)在當前線程中加入調(diào)用join方法的線程A,直到到達參數(shù)指定毫秒數(shù)或線程A死亡才能繼續(xù)執(zhí)行當前線程8.1.2Thread類(續(xù))
——常用API函數(shù)多線程編程基礎(chǔ)12publicfinalvoidsetPriority(int
newPriority)設(shè)置線程優(yōu)先級publicfinalvoidsetDaemon(Booleanon)設(shè)置是否為后臺線程,如果當前運行線程均為后臺線程則JVM停止運行。這個方法必須在start()方法前使用publicfinalvoidcheckAccess()判斷當前線程是否有權(quán)力修改調(diào)用此方法的線程publicvoidsetName(Stringname)更該本線程的名稱為指定參數(shù)publicfinalboolean
isAlive()測試線程是否處于活動狀態(tài),如果線程被啟動并且沒有死亡則返回true8.1.2Thread類(續(xù))
——常用API函數(shù)多線程編程基礎(chǔ)13創(chuàng)建3個新線程,每個線程睡眠一段時間(0~6秒),然后結(jié)束publicclassEx8_2{publicstaticvoidmain(String[]args){//創(chuàng)建并命名每個線程
TestThreadthread1=newTestThread("thread1");
TestThreadthread2=newTestThread("thread2");
TestThreadthread3=newTestThread("thread3");
System.out.println("Startingthreads");thread1.start();//啟動線程1thread2.start();//啟動線程2thread3.start();//啟動線程3
System.out.println("Threadsstarted,mainends\n");}}8.1.2Thread類(續(xù))
——例8_2多線程編程基礎(chǔ)14classTestThread
extendsThread{privateint
sleepTime;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_2多線程編程基礎(chǔ)15運行結(jié)果StartingthreadsThreadsstarted,mainendsthread1goingtosleepfor3519thread2goingtosleepfor1689thread3goingtosleepfor5565thread2finishedthread1finishedthread3finished說明由于線程3休眠時間最長,所以最后結(jié)束,線程2休眠時間最短,所以最先結(jié)束每次運行,都會產(chǎn)生不同的隨機休眠時間,所以結(jié)果都不相同8.1.2Thread類(續(xù))
——例8_2運行結(jié)果多線程編程基礎(chǔ)168.1.3Runnable接口Runnable接口Java多線程機制的一個重要部分,實際上它只有一個run()方法Thread類實現(xiàn)了Runnable接口,相對于Thread類,它更適合于多個線程處理同一資源實現(xiàn)Runnable接口的類的對象可以用來創(chuàng)建線程,這時start方法啟動此線程就會在此線程上運行run()方法在編寫復(fù)雜程序時相關(guān)的類可能已經(jīng)繼承了某個基類,而Java不支持多繼承,在這種情況下,便需要通過實現(xiàn)Runnable接口來生成多線程多線程編程基礎(chǔ)17使用Runnable接口實現(xiàn)例8_1功能publicclassEx8_1{publicstaticvoidmain(String[]args){
System.out.println("mainthreadstarts");
FactorialThreadt=newFactorialThread(10);
newThread(t).start();
System.out.println("newthreadstarted,mainthreadends");}}8.1.3Runnable接口(續(xù))
——例8_3多線程編程基礎(chǔ)18classFactorialThreadimplementsRunnable{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_3多線程編程基礎(chǔ)19使用Runnable接口實現(xiàn)例8_2功能publicclassEx8_4{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_4多線程編程基礎(chǔ)20classTestThreadimplementsRunnable{privateint
sleepTime;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.3Runnable接口(續(xù))
——例8_4多線程編程基礎(chǔ)218.1.4線程間的數(shù)據(jù)共享代碼共享多個線程的執(zhí)行代碼來自同一個類的run方法時,即稱它們共享相同的代碼數(shù)據(jù)共享當共享訪問相同的對象時,即它們共享相同的數(shù)據(jù)使用Runnable接口可以輕松實現(xiàn)多個線程共享相同數(shù)據(jù),只要用同一個實現(xiàn)了Runnable接口的實例作為參數(shù)創(chuàng)建多個線程就可以了多線程編程基礎(chǔ)22修改例8_4,只用一個Runnable類型的對象為參數(shù)創(chuàng)建3個新線程。publicclassEx8_5{publicstaticvoidmain(String[]args){
TestThread
threadobj=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_5多線程編程基礎(chǔ)23classTestThreadimplementsRunnable{ privateint
sleepTime; 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_5多線程編程基礎(chǔ)24運行結(jié)果StartingthreadsThread1goingtosleepfor966Thread2goingtosleepfor966Threadsstarted,mainendsThread3goingtosleepfor966Thread1finishedThread2finishedThread3finished說明因為是用一個Runnable類型對象創(chuàng)建的3個新線程,這三個線程就共享了這個對象的私有成員sleepTime,在本次運行中,三個線程都休眠了966毫秒8.1.4線程間的數(shù)據(jù)共享(續(xù))
——例8_5運行結(jié)果多線程編程基礎(chǔ)258.1.4線程間的資源共享(續(xù))獨立的同時運行的線程有時需要共享一些數(shù)據(jù)并且考慮到彼此的狀態(tài)和動作例如生產(chǎn)/消費問題:生產(chǎn)線程產(chǎn)生數(shù)據(jù)流,然后這些數(shù)據(jù)流再被消費線程消費假設(shè)一個Java應(yīng)用程序,其中有一個線程負責往文件寫數(shù)據(jù),另一個線程從同一個文件中往出都數(shù)據(jù),因為涉及到同一個資源,這里是同一個文件,這兩個線程必須保證某種方式的同步多線程編程基礎(chǔ)26用三個線程模擬三個售票口,總共出售200張票用3個線程模仿3個售票口的售票行為這3個線程應(yīng)該共享200張票的數(shù)據(jù)publicclassEx8_6{ publicstaticvoidmain(String[]args){
SellTicketst=newSellTickets();newThread(t).start();
newThread(t).start(); newThread(t).start(); }}多線程編程基礎(chǔ)8.1.4線程間的數(shù)據(jù)共享(續(xù))
——例8_627classSellTicketsimplementsRunnable{ privateinttickets=200;
publicvoidrun(){
while(tickets>0){
System.out.println(Thread.currentThread().getName()+ "issellingticket"+tickets--); } }}8.1.4線程間的數(shù)據(jù)共享(續(xù))
——例8_6多線程編程基礎(chǔ)28運行結(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é)果多線程編程基礎(chǔ)298.1.5多線程的同步控制有時線程之間彼此不獨立、需要同步線程間的互斥同時運行的幾個線程需要共享一個(些)數(shù)據(jù)一個線程對共享的數(shù)據(jù)進行操作時,不允許其他線程打斷它,否則會破壞數(shù)據(jù)的完整性。即被多個線程共享的數(shù)據(jù),在某一時刻只允許一個線程對其進行操作“生產(chǎn)者/消費者”問題生產(chǎn)者產(chǎn)生數(shù)據(jù),消費者消費數(shù)據(jù),具體來說,假設(shè)有一個Java應(yīng)用程序,其中有一個線程負責往數(shù)據(jù)區(qū)寫數(shù)據(jù),另一個線程從同一數(shù)據(jù)區(qū)中讀數(shù)據(jù),兩個線程可以并行執(zhí)行(類似于流水線上的兩道工序)如果數(shù)據(jù)區(qū)已滿,,生產(chǎn)者要等消費者取走一些數(shù)據(jù)后才能再放;而當數(shù)據(jù)區(qū)沒有數(shù)據(jù)時,消費者要等生產(chǎn)者放入一些數(shù)據(jù)后再取多線程編程基礎(chǔ)30用兩個線程模擬存票、售票過程假定開始售票處并沒有票,一個線程往里存票,另外一個線程則往出賣票我們新建一個票類對象,讓存票和售票線程都訪問它。本例采用兩個線程共享同一個數(shù)據(jù)對象來實現(xiàn)對同一份數(shù)據(jù)的操作publicclassEx8_7{ publicstaticvoidmain(String[]args){ Ticketst=newTickets(10); newConsumer(t).start();
newProducer(t).start();
}}8.1.5多線程的同步控制(續(xù))
——例8_7多線程編程基礎(chǔ)31classTickets{
intnumber=0;//票號
intsize;//總票數(shù)
booleanavailable=false;//表示目前是否有票可售
publicTickets(intsize)//構(gòu)造函數(shù),傳入總票數(shù)參數(shù)
{
this.size=size; } }8.1.5多線程的同步控制(續(xù))
——例8_7多線程編程基礎(chǔ)32classProducerextendsThread{ 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_7多線程編程基礎(chǔ)33classConsumerextendsThread//售票線程{ 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.1.5多線程的同步控制(續(xù))
——例8_7多線程編程基礎(chǔ)34運行結(jié)果Producerputsticket1Producerputsticket2Producerputsticket3Producerputsticket4Producerputsticket5Producerputsticket6Producerputsticket7Producerputsticket8Consumerbuysticket1Consumerbuysticket2Consumerbuysticket3Consumerbuysticket4Consumerbuysticket5Consumerbuysticket6Consumerbuysticket7Consumerbuysticket8Producerputsticket9Producerputsticket10Consumerbuysticket9Consumerbuysticket10.通過讓兩個線程操縱同一個票類對象,實現(xiàn)了數(shù)據(jù)共享的目的8.1.5多線程的同步控制(續(xù))
——例8_7運行結(jié)果多線程編程基礎(chǔ)35設(shè)想一下,假如售票線程運行到t.available=false之前,CPU切換到存票線程,存票線程將available置為true,并直到整個存票線程結(jié)束。再次切換到售票線程后,售票線程執(zhí)行t.available=false。此時售票號小于存票數(shù),且存票線程已經(jīng)結(jié)束不再能將t.available置為true,則售票線程陷入了死循環(huán)如果我們在t.available=false之前加上sleep語句,讓售票線程多停留一會,則可以更加清楚地看到這個問題if(i==t.number){try{Thread.sleep(1);}catch(InterruptedExceptionexception){};
t.available=false;}8.1.5多線程的同步控制(續(xù))
——例8_7修改多線程編程基礎(chǔ)36修改后運行結(jié)果Producerputsticket1Producerputsticket2Producerputsticket3Producerputsticket4Producerputsticket5Producerputsticket6Producerputsticket7Producerputsticket8Consumerbuysticket1Consumerbuysticket2Consumerbuysticket3Consumerbuysticket4Consumerbuysticket5Consumerbuysticket6Consumerbuysticket7Consumerbuysticket8Producerputsticket9Producerputsticket108.1.5多線程的同步控制(續(xù))
——例8_7修改后運行結(jié)果多線程編程基礎(chǔ)37如何避免上面這種意外,讓我們的程序是“線程安全”的呢?解決線程的同步/互斥問題存票線程和售票線程應(yīng)保持互斥關(guān)系。即售票線程執(zhí)行時不進入存票線程、存票線程執(zhí)行時不進入售票線程Java使用的同步機制是監(jiān)視器每個對象都只有一個“鎖旗標”與之相連,利用多線程對其的爭奪可實現(xiàn)線程間的互斥操作當線程A獲得了一個對象的鎖旗標后,線程B必須等待線程A完成規(guī)定的操作、并釋放出鎖旗標后,才能獲得該對象的鎖旗標,并執(zhí)行線程B中的操作8.1.5多線程的同步控制(續(xù))
——解決例8_7的問題多線程編程基礎(chǔ)38線程同步的概念,包括互斥和協(xié)作互斥:許多線程在同一個共享數(shù)據(jù)上操作而互不干擾,同一時刻只能有一個線程訪問該共享數(shù)據(jù)。因此有些方法或程序段在同一時刻只能被一個線程執(zhí)行,稱之為監(jiān)視區(qū)協(xié)作:多個線程可以有條件地同時操作共享數(shù)據(jù)。執(zhí)行監(jiān)視區(qū)代碼的線程在條件滿足的情況下可以允許其它線程進入監(jiān)視區(qū)8.1.5多線程的同步控制(續(xù))
——線程同步(Synchronization)多線程編程基礎(chǔ)39synchronized——線程同步關(guān)鍵字用于指定需要同步的代碼段或方法,也就是監(jiān)視區(qū)可實現(xiàn)與一個鎖旗標的交互。例如:synchronized(對象){代碼段}synchronized的功能是:首先判斷對象的鎖旗標是否在,如果在就獲得鎖旗標,然后就可以執(zhí)行緊隨其后的代碼段;如果對象的鎖旗標不在(已被其他線程拿走),就進入等待狀態(tài),直到獲得鎖旗標當被synchronized限定的代碼段執(zhí)行完,就釋放鎖旗標8.1.5多線程的同步控制(續(xù))
——synchronized關(guān)鍵字多線程編程基礎(chǔ)40將需要互斥的語句段放入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ù))
——synchronized關(guān)鍵字多線程編程基礎(chǔ)41classConsumerextendsThread{ 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ù))
——synchronized關(guān)鍵字多線程編程基礎(chǔ)42說明存票程序段和售票程序段為獲得同一對象的鎖旗標而實現(xiàn)互斥操作當線程執(zhí)行到synchronized的時候,檢查傳入的實參對象,并申請得到該對象的鎖旗標。如果得不到,那么線程就被放到一個與該對象鎖旗標相對應(yīng)的等待線程池中。直到該對象的鎖旗標被歸還,池中的等待線程才能重新去獲得鎖旗標,然后繼續(xù)執(zhí)行下去除了可以對指定的代碼段進行同步控制之外,還可以定義整個方法在同步控制下執(zhí)行,只要在方法定義前加上synchronized關(guān)鍵字即可8.1.5多線程的同步控制(續(xù))
——synchronized關(guān)鍵字多線程編程基礎(chǔ)43實現(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_8多線程編程基礎(chǔ)44說明同步方法使用的鎖旗標關(guān)聯(lián)對象正是方法所屬的實例對象。在例8_8中,正是因為put和sell兩個同步方法都屬于同一個Tickets類的對象,所以實現(xiàn)了同步由于要實現(xiàn)多線程的數(shù)據(jù)共享,即多個線程對同一數(shù)據(jù)資源進行操作,就可能造成一個線程對資源進行了部分處理,另一個線程就插進來對其進行處理,這樣就會破壞共享數(shù)據(jù)的完整性。因此,需要使用線程同步與互斥技術(shù),防止不同的線程同時對共享數(shù)據(jù)進行修改操作。數(shù)據(jù)共享和線程互斥操作經(jīng)常是密不可分的8.1.5多線程的同步控制(續(xù))
——例8_8說明多線程編程基礎(chǔ)458.1.6線程之間的通信為了更有效地協(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í)行)多線程編程基礎(chǔ)468.1.6線程之間的通信(續(xù))
——notify()和notifyAll()方法notify()隨機喚醒一個等待的線程,本線程繼續(xù)執(zhí)行線程被喚醒以后,還要等發(fā)出喚醒消息者釋放監(jiān)視器,這期間關(guān)鍵數(shù)據(jù)仍可能被改變被喚醒的線程開始執(zhí)行時,一定要判斷當前狀態(tài)是否適合自己運行notifyAll()喚醒所有等待的線程,本線程繼續(xù)執(zhí)行多線程編程基礎(chǔ)47修改例8_8,使每存入一張票,就售一張票,售出后,再存入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;//在售完最后一張票后,
//設(shè)置一個結(jié)束標志,number>size表示售票結(jié)束
} }8.1.6線程之間的通信(續(xù))
——例8_9多線程編程基礎(chǔ)48classProducerextendsThread{ 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.6線程之間的通信(續(xù))
——例8_9多線程編程基礎(chǔ)49運行結(jié)果Producerputsticket1Consumerbuysticket1Producerputsticket2Consumerbuysticket2Producerputsticket3Consumerbuysticket3Producerputsticket4Consumerbuysticket4Producerputsticket5Consumerbuysticket5Producerputsticket6Consumerbuysticket6Producerputsticket7Consumerbuysticket7Producerputsticket8Consumerbuysticket8Producerputsticket9Consumerbuysticket9Producerputsticket10Consumerbuysticket108.1.6線程之間的通信(續(xù))
——例8_9運行結(jié)果多線程編程基礎(chǔ)50程序說明當Consumer線程售出票后,available值變?yōu)閒alse,當Producer線程放入票后,available值變?yōu)閠rue只有available為true時,Consumer線程才能售票,否則就必須等待Producer線程放入新的票后的通知只有available為false時,Producer線程才能放票,否則必須等待Consumer線程售出票后的通知可見通過線程間的通信實現(xiàn)了我們的要求8.1.6線程之間的通信(續(xù))
——例8_9說明多線程編程基礎(chǔ)518.1.7后臺線程后臺線程也叫守護線程,通常是為了輔助其它線程而運行的線程它不妨礙程序終止一個進程中只要還有一個前臺線程在運行,這個進程就不會結(jié)束;如果一個進程中的所有前臺線程都已經(jīng)結(jié)束,那么無論是否還有未結(jié)束的后臺線程,這個進程都會結(jié)束“垃圾回收”便是一個后臺線程如果對某個線程對象在啟動(調(diào)用start方法)之前調(diào)用了setDaemon(true)方法,這個線程就變成了后臺線程多線程編程基礎(chǔ)52創(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.7后臺線程(續(xù))
——例8_10多線程編程基礎(chǔ)538.2線程的生命周期線程的生命周期線程從產(chǎn)生到消亡的過程一個線程在任何時刻都處于某種線程狀態(tài)(threadstate)548.2.1線程的幾種基本狀態(tài)線程生命周期狀態(tài)圖線程的生命周期55誕生狀態(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ù))線程的生命周期568.2.2死鎖問題死鎖線程在運行過程中,其中某個步驟往往需要滿足一些條件才能繼續(xù)進行下去,如果這個條件不能滿足,線程將在這個步驟上出現(xiàn)阻塞線程A可能會陷于對線程B的等待,而線程B同樣陷于對線程C的等待,依次類推,整個等待鏈最后又可能回到線程A。如此一來便陷入一個彼此等待的輪回中,任何線程都動彈不得,此即所謂死鎖(deadlock)對于死鎖問題,關(guān)鍵不在于出現(xiàn)問題后調(diào)試,而是在于預(yù)防線程的生命周期57設(shè)想一個游戲,規(guī)則為3個人站在三角形的三個頂點的位置上,三個邊上放著三個球,如圖所示。每個人都必須先拿到自己左手邊的球,才能再拿到右手邊的球,兩手都有球之后,才能夠把兩個球都放下Player_0Player_1Player_20218.2.2死鎖問題(續(xù))
——例8_11線程的生命周期58例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.2死鎖問題(續(xù))
——例8_11線程的生命周期59classPlayer0extendsThread{//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.2死鎖問題(續(xù))
——例8_11線程的生命周期60運行結(jié)果若干次后將陷入死鎖,不再有輸出信息,即任何人都不能再同時擁有兩側(cè)的球程序說明如果剛好3個人都拿到了左手邊的球,都等待那右手邊的球,則因為誰都不能放手,則這3個線程都將陷入無止盡的等待當中,這就構(gòu)成了死鎖為了便于觀察死鎖發(fā)生的條件,我們在每個游戲者放下兩邊的球后增加了sleep語句為了避免死鎖,需要修改游戲規(guī)則,使每個人都只能先搶到兩側(cè)中號比較小的球,才能拿另一只球,這樣就不會再出現(xiàn)死鎖現(xiàn)象8.2.2死鎖問題(續(xù))
——例8_11運行結(jié)果線程的生命周期618.2.3控制線程的生命結(jié)束線程的生命用stop方法可以結(jié)束線程的生命但如果一個線程正在操作共享數(shù)據(jù)段,操作過程沒有完成就用stop結(jié)束的話,將會導(dǎo)致數(shù)據(jù)的不完整,因此并不提倡使用此方法通常,可通過控制run方法中循環(huán)條件的方式來結(jié)束一個線程線程的生命周期62線程不斷顯示遞增整數(shù),按下回車鍵則停止執(zhí)行importjava.io.*;publicclassEx8_12{ publicstaticvoidmain(String[]args)throwsIOException{
TestThreadt=newTestThread();
t.start(); newBufferedReader(new
InputStreamReader(System.in)).readLine();//等待鍵盤輸入
t.stopme();//調(diào)用stopme方法結(jié)束t線程
}}8.2.3控制線程的生命(續(xù))
——例8_12線程的生命周期63classTestThreadextendsThread{ privatebooleanflag=true; publicvoidstopme(){//在此方法中控制循環(huán)條件
flag=false; } publicvoidrun(){
inti=0;
while(flag){
System.out.println(i++);//如果flag為真則一直顯示遞增整數(shù)
} }}運行效果為按下回車鍵后則停止顯示8.2.3控制線程的生命(續(xù))
——例8_12線程的生命周期648.3線程的優(yōu)先級線程調(diào)度在單CPU的系統(tǒng)中,多個線程需要共享CPU,在任何時間點上實際只能有一個線程在運行控制多個線程在同一個CPU上以某種順序運行稱為線程調(diào)度Java虛擬機支持一種非常簡單的、確定的調(diào)度算法,叫做固定優(yōu)先級算法。這個算法基于線程的優(yōu)先級對其進行調(diào)度65線程的優(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.3線程的優(yōu)先級(續(xù))線程的優(yōu)先級66基于線程優(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ù))線程的優(yōu)先級67假設(shè)某線程正在運行,則只有出現(xiàn)以下情況之一,才會使其暫停運行一個具有更高優(yōu)先級的線程變?yōu)榫途w狀態(tài)(Ready);由于輸入/輸出(或其他一些原因)、調(diào)用sleep、wait、yield方法使其發(fā)生阻塞;對于支持時間分片的系統(tǒng),時間片的時間期滿8.3線程的優(yōu)先級(續(xù))線程的優(yōu)先級68創(chuàng)建兩個具有不同優(yōu)先級的線程,都從1遞增到400000,每增加50000顯示一次publicclassEx8_13{publicstaticvoidmain(String[]args){
TestThread[]runners=newTestThread[2];for(
溫馨提示
- 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)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 化學(xué)自主訓(xùn)練第二單元課題獲取潔凈的水
- 專項訓(xùn)練:有余數(shù)的除法計算題(含答案)-蘇教版二年級數(shù)學(xué)下冊
- 語法填空高頻話題(哲理與感悟)-2024年新高考英語一輪復(fù)習(xí)
- 2025年關(guān)節(jié)器械試題及答案
- 2025年保安員(初級)考試模擬題及答案
- 2025年【公路水運工程施工企業(yè)安全生產(chǎn)管理人員】考試題及答案
- 危險品運輸人員培訓(xùn)考核試卷
- 重難點01 閱讀理解推理判斷題-2024年高考英語(新高考專用)原卷版
- 保健品市場品牌服務(wù)創(chuàng)新與跨界合作模式考核試卷
- 綠色創(chuàng)意發(fā)展考核試卷
- 2025年靜寧縣城區(qū)學(xué)校選調(diào)教師考試筆試試卷【附答案】
- 2025年樂清輔警考試題庫及答案
- 2025年長春市事業(yè)單位招聘考試綜合類專業(yè)能力測試試卷(管理類)
- 2025年工業(yè)和信息化部所屬事業(yè)單位招聘28人筆試模擬試題及答案詳解一套
- 2025年全國國家版圖知識競賽測試題庫(中小學(xué)組)及參考答案詳解【完整版】
- 風力發(fā)電項目投資計劃書
- 2025年康復(fù)理療師專項能力證書考試真題卷(后附答案和解析)
- 2025年度食堂餐具設(shè)備升級改造采購合同
- GB/T 45938-2025醫(yī)療保障信息平臺便民服務(wù)相關(guān)技術(shù)規(guī)范
- 河北公物拍賣管理辦法
- 供排水調(diào)度工公司招聘筆試題庫及答案
評論
0/150
提交評論