Java課件第八章線程_第1頁
Java課件第八章線程_第2頁
Java課件第八章線程_第3頁
Java課件第八章線程_第4頁
Java課件第八章線程_第5頁
已閱讀5頁,還剩201頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

第八章線程

本章導(dǎo)讀1.Java中的線程2.線程的生命周期3.線程的優(yōu)先級(jí)與調(diào)度管理4.Thread的子類創(chuàng)建線程5.使用Runable接口6.線程的常用方法7.線程同步8.在同步方法中使用wait()、notify和notifyAll9.線程的聯(lián)合10.守護(hù)線程第八章線程本章導(dǎo)讀18.1Java中的線程程序是一段靜態(tài)的代碼,它是應(yīng)用軟件執(zhí)行的藍(lán)本。進(jìn)程是程序的一次動(dòng)態(tài)執(zhí)行過程,它對(duì)應(yīng)了從代碼加載、執(zhí)行至執(zhí)行完畢的一個(gè)完整過程,這個(gè)過程也是進(jìn)程本身從產(chǎn)生、發(fā)展至消亡的過程。線程是比進(jìn)程更小的執(zhí)行單位。一個(gè)進(jìn)程在其執(zhí)行過程中,可以產(chǎn)生多個(gè)線程,形成多條執(zhí)行線索,每條線索,即每個(gè)線程也有它自身的產(chǎn)生、存在和消亡的過程,也是一個(gè)動(dòng)態(tài)的概念。為理解線程這個(gè)概念我們看一段程序:8.1Java中的線程程序是一段靜態(tài)28.1Java中的線程ClassQution{publicstaticvoidmain(Stringargs[]){while(true){System.outprintln(“123”);}while(true){System.outprintln(“abc”);}}}第2個(gè)while是不會(huì)執(zhí)行的,但將如創(chuàng)建兩個(gè)線程Java的虛擬機(jī)會(huì)負(fù)責(zé)使倆、個(gè)線程輪流執(zhí)行。8.1Java中的線程ClassQution{3

Java應(yīng)用程序總是從主類的main方法開始執(zhí)行。當(dāng)JVM加載代碼,發(fā)現(xiàn)main方法之后,就會(huì)啟動(dòng)一個(gè)線程,這個(gè)線程稱作“主線程”,該線程負(fù)責(zé)執(zhí)行main方法。那么,在main方法中再創(chuàng)建的線程,就稱為主線程中的線程。如果main方法中沒有創(chuàng)建其他的線程,那么當(dāng)main方法執(zhí)行完最后一個(gè)語句,即main方法返回時(shí),JVM就會(huì)結(jié)束我們的Java應(yīng)用程序。如果main方法中又創(chuàng)建了其他線程,那么JVM就要在主線程和其他線程之間輪流切換,保證每個(gè)線程都有機(jī)會(huì)使用CPU資源,main方法即使執(zhí)行完最后的語句,JVM也不會(huì)結(jié)束我們的程序,JVM一直要等到主線程中的所有線程都結(jié)束之后,才結(jié)束我們的Java應(yīng)用程序。返回Java應(yīng)用程序總是從主類的main方法開48.2線程的生命周期

1.線程的4種狀態(tài)在Java語言中,Thread類及其子類創(chuàng)建的對(duì)象稱作線程,新建的線程在它的一個(gè)完整的生命周期中通常要經(jīng)歷4種狀態(tài),(1)新建(2)運(yùn)行線程創(chuàng)建后僅僅是占有了內(nèi)存資源,在JVM管理的線程中還沒有這個(gè)線程,此線程必須調(diào)用start()方法(從父類繼承的方法)通知JVM,這樣JVM就會(huì)知道又有一個(gè)新一個(gè)線程排隊(duì)等候切換了。8.2線程的生命周期1.線程的4種狀態(tài)5

當(dāng)JVM將CUP使用權(quán)切換給線程時(shí),如果線程是Thread的子類創(chuàng)建的,該類中的run方法就立刻執(zhí)行。所以我們必須在子類中重寫父類的run方法,Thread類中的run()方法沒有具體內(nèi)容,程序要在Thread類的子類中重寫run()方法來覆蓋父類的run()方法,run方法規(guī)定了該線程的具體使命。在線程沒有結(jié)束run方法之前,不要讓線程再調(diào)用start方法,否則將發(fā)生ILLegalThreadStateException異常。當(dāng)JVM將CUP使用權(quán)切換給線程時(shí),如果6(3)中斷有4種原因的中斷:(a)JVM將CPU資源從當(dāng)前線程切換給其他線程,使本線程讓出CPU的使用權(quán)處于中斷狀態(tài)。(b)線程使用CPU資源期間,執(zhí)行了sleep(intmillsecond)方法,使當(dāng)前線程進(jìn)入休眠狀態(tài)。sleep(intmillsecond)方法是Thread類中的一個(gè)類方法,線程一旦執(zhí)行了sleep(intmillsecond)方法,就立刻讓出CPU的使用權(quán),使當(dāng)前線程處于中斷狀態(tài)。經(jīng)過參數(shù)millsecond指定的豪秒數(shù)之后,該線程就重新進(jìn)到線程隊(duì)列中排隊(duì)等待CPU資源,以便從中斷處繼續(xù)運(yùn)行。(3)中斷7(c)線程使用CPU資源期間,執(zhí)行了wait()方法,使得當(dāng)前線程進(jìn)入等待狀態(tài)。等待狀態(tài)的線程不會(huì)主動(dòng)進(jìn)到線程隊(duì)列中排隊(duì)等待CPU資源,必須由其他線程調(diào)用notify()方法通知它,使得它重新進(jìn)到線程隊(duì)列中排隊(duì)等待CPU資源,以便從中斷處繼續(xù)運(yùn)行。有關(guān)wait、noftify和notifyAll方法將在第8節(jié)詳細(xì)討論(d)線程使用CPU資源期間,執(zhí)行某個(gè)操作進(jìn)入阻塞狀態(tài),比如執(zhí)行讀/寫操作引起阻塞。進(jìn)入阻塞狀態(tài)時(shí)線程不能進(jìn)入排隊(duì)隊(duì)列,只有當(dāng)引起阻塞的原因消除時(shí),線程才重新進(jìn)到線程隊(duì)列中排隊(duì)等待CPU資源,以便從原來中斷處開始繼續(xù)運(yùn)行。

(c)線程使用CPU資源期間,執(zhí)行了wait()方法,使得當(dāng)8

(4)死亡處于死亡狀態(tài)的線程不具有繼續(xù)運(yùn)行的能力。線程死亡的原因有二,一個(gè)是正常運(yùn)行的線程完成了它的全部工作,即執(zhí)行完run方法中的全部語句,結(jié)束了run方法。另一個(gè)原因是線程被提前強(qiáng)制性地終止,即強(qiáng)制run方法結(jié)束。所謂死亡狀態(tài)就是線程釋放了實(shí)體,即釋放分配給線程對(duì)象的內(nèi)存。現(xiàn)在,我們看一個(gè)完整的例子1,通過分析運(yùn)行結(jié)果闡述線程的4種狀態(tài)。該例子中我們用Thread的子類:WriteWordThread創(chuàng)建了兩個(gè)線程。(4)死亡9publicclassExample8_1{publicstaticvoidmain(Stringargs[]){WriteWordThreadzhang,wang;zhang=newWriteWordThread("張小紅");//新建線程wang=newWriteWordThread("JamsKeven");//新建線程zhang.start();//啟動(dòng)線程for(inti=1;i<=8;i++){System.out.println("我是主線程中的語句");}wang.start();//啟動(dòng)線程}}publicclassExample8_1{10classWriteWordThreadextendsThread{//Thread的子類負(fù)責(zé)創(chuàng)建線程對(duì)象WriteWordThread(Strings){setName(s);//調(diào)用Thread類的setName方法為線程起個(gè)名字}publicvoidrun(){for(inti=1;i<=8;i++){System.out.println("我是一個(gè)線程,我的名字是"+getName());}}}Java課件第八章線程11

上述程序在不同的計(jì)算機(jī)運(yùn)行或在同一臺(tái)計(jì)算機(jī)反復(fù)運(yùn)行的結(jié)果不盡相同,輸出結(jié)果依賴當(dāng)前CPU資源的使用情況。為了使結(jié)果盡量不依賴于當(dāng)前CPU資源的使用情況,我們應(yīng)當(dāng)讓線程主動(dòng)調(diào)用sleep方法讓出CPU的使用權(quán)進(jìn)入中斷狀態(tài),如例子2所示:

publicclassExample8_2{publicstaticvoidmain(Stringargs[]){WriteWordThreadzhang,wang;

返回上述程序在不同的計(jì)算機(jī)運(yùn)行或在同一臺(tái)計(jì)算機(jī)反復(fù)運(yùn)行的結(jié)12zhang=newWriteWordThread("張小紅",200);//新建線程wang=newWriteWordThread("JamsKeven",100);//新建線程zhang.start();//啟動(dòng)線程wang.start();//啟動(dòng)線程}}classWriteWordThreadextendsThread{intn=0;WriteWordThread(Strings,intn){返回zhang=newWriteWordThr13

setName(s);//調(diào)用Thread類的方法setName為線程起個(gè)名字this.n=n;}publicvoidrun(){for(inti=1;i<=8;i++){System.out.println("我是一個(gè)線程,我的名字是"+getName());try{sleep(n);}catch(InterruptedExceptione){}}}}返回setName(s);//調(diào)用T148.3線程的優(yōu)先級(jí)與調(diào)度管理

Java虛擬機(jī)(JVM)中的線程調(diào)度器負(fù)責(zé)管理線程,調(diào)度器把線程的優(yōu)先級(jí)分為10個(gè)級(jí)別,分別用Thread類中的類常量表示。每個(gè)Java線程的優(yōu)先級(jí)都在常數(shù)1:Thread.MINPRIORITY到常數(shù)10:Thread.MAX_PRIORITY的范圍內(nèi)。如果沒有明確地設(shè)置線程的優(yōu)先級(jí)別,每個(gè)線程的優(yōu)先級(jí)都為常數(shù)5(包括主線程):Thread.NORM_PRIORITY,線程的優(yōu)先級(jí)可以通過setPriority(intgrade)方法調(diào)整,這一方法需要一個(gè)int類型參數(shù)。如果此參數(shù)不在1~10的范圍內(nèi),那么setPriority便產(chǎn)生一個(gè)lllegalArgumenException異常。getPriority方法返回線程的優(yōu)先級(jí)。需要注意是,有些操作系統(tǒng)只能識(shí)別3個(gè)級(jí)別:1,5,10。返回8.3線程的優(yōu)先級(jí)與調(diào)度管理Java158.4Thread的子類創(chuàng)建線程

在Java語言中,用Thread類或子類創(chuàng)建線程對(duì)象。這一節(jié)講述怎樣用Thread子類創(chuàng)建對(duì)象。用戶可以擴(kuò)展Thread類,但需要重寫父類的run方法,其目的是規(guī)定線程的具體操作,否則線程就什么也不做,因?yàn)楦割惖膔un方法中沒有任何操作語句。下面例子3中除主線程外還有兩個(gè)線程,這兩個(gè)線程分別在命令行窗口的左側(cè)和右側(cè)順序地一行一行地輸出字符串。主線程負(fù)責(zé)判斷輸出的行數(shù),當(dāng)其中任何一個(gè)線程輸出8行后,就結(jié)束進(jìn)程。本例題中用到了System類中的類方法:exit(intn),主線程使用該方法結(jié)束整個(gè)程序。publicclassExample8_3{publicstaticvoidmain(Stringargs[]){

返回8.4Thread的子類創(chuàng)建線程在J16Leftleft=newLeft();Rightright=newRight();left.start();right.start();while(true){if(left.n==8||right.n==8)System.exit(0);}}}classLeftextendsThread{intn=0;publicvoidrun(){while(true){n++;返回Leftleft=newLeft();返178.4Thread的子類創(chuàng)建線程System.out.printf("\n%s","我在左面寫字");try{sleep((int)(Math.random()*100)+100);}catch(InterruptedExceptione){}}}}classRightextendsThread{intn=0;publicvoidrun(){返回8.4Thread的子類創(chuàng)建線程188.4Thread的子類創(chuàng)建線程while(true){n++;System.out.printf("\n%40s","我在右面寫字");try{sleep((int)(Math.random()*100)+100);}catch(InterruptedExceptione){}}}}返回8.4Thread的子類創(chuàng)建線程whil198.5使用Runable接口

使用Thread子類創(chuàng)建線程的優(yōu)點(diǎn)是:我們可以在子類中增加新的成員變量,使線程具有某種屬性,也可以在子類中新增加方法,使線程具有某種功能。但是,Java不支持多繼承,Thread類的子類不能再擴(kuò)展其他的類。8.5使用Runable接口使用Th20

1.Runnable接口與目標(biāo)對(duì)象創(chuàng)建線程的另一個(gè)途徑就是用Thread類直接創(chuàng)建線程對(duì)象。使用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方法后,一旦輪到它來享用CPU資源,目標(biāo)對(duì)象就會(huì)自動(dòng)調(diào)用接口中的run方法(接口回調(diào)),這一過程是自動(dòng)實(shí)現(xiàn)的,用戶程序只需要讓線程調(diào)用start方法即可,也就是說,當(dāng)線程被調(diào)度并轉(zhuǎn)入運(yùn)行狀態(tài)時(shí),所執(zhí)行的就是run()方法中所規(guī)定的操作。1.Runnable接口與目標(biāo)對(duì)象21

線程間可以共享相同的內(nèi)存單元(包括代碼與數(shù)據(jù)),并利用這些共享單元實(shí)現(xiàn)數(shù)據(jù)的交換、實(shí)時(shí)通訊與必要的同步操作。對(duì)于thread(Runnabletarget)構(gòu)造的線程使用同一目標(biāo)對(duì)象的線程,目標(biāo)對(duì)象的成員變量自然成為這些線程共享的數(shù)據(jù)單元。線程間可以共享相同的內(nèi)存單元(包括代碼與數(shù)據(jù)),22

下面的例子4中,兩個(gè)線程:zhang和cheng,使用同一目標(biāo)對(duì)象(Bank類對(duì)象)。兩個(gè)線程共享目標(biāo)對(duì)象的money。當(dāng)money的值小于100時(shí),線程zhang結(jié)束自己的run方法進(jìn)入死亡狀態(tài);當(dāng)money的值小于60時(shí),線程cheng結(jié)束自己的run方法進(jìn)入死亡狀態(tài)。publicclassExample8_4{publicstaticvoidmain(Stringargs[]){Strings1="會(huì)計(jì)",s2="出納";Bankbank=newBank(s1,s2);

23Threadzhang,cheng;zhang=newThread(bank);cheng=newThread(bank);//cheng和zhang是同一目標(biāo)對(duì)象zhang.setName(s1);//會(huì)計(jì)cheng.setName(s2);//出納bank.setMoney(160);//線程的目標(biāo)對(duì)象修改被線程共享的moneyzhang.start();cheng.start();}}Threadzhang,cheng;24classBankimplementsRunnable{

//Bank類必須實(shí)現(xiàn)Runnable接口privateintmoney=0;Stringname1,name2;Bank(Strings1,Strings2){name1=s1;name2=s2;}publicvoidsetMoney(intmount){money=mount;}classBankimplementsRunnable25publicvoidrun(){//接口中的方法while(true){money=money-10;if(Thread.currentThread().getName().equals(name1)){System.out.println("我是"+name1+"現(xiàn)在有"+money+"元");if(money<=100){System.out.println(name1+“進(jìn)入死亡狀態(tài)”);return;//如果money小于100,當(dāng)前線程結(jié)束run方法}}publicvoidrun(){26elseif(Thread.currentThread().getName().equals(name2)){System.out.println("我是"+name2+"現(xiàn)在有"+money+"元");if(money<=60){System.out.println(name2+"進(jìn)入死亡狀態(tài)");return;//如果money小于60,當(dāng)前線程結(jié)束run方法}}elseif(Thread.curren27try{Thread.sleep(800);}catch(InterruptedExceptione){}}}}try{28

例子5中共有4個(gè)線程:threadA、threadB、threadC和threadD,其中threadA和threadB的目標(biāo)對(duì)象a1,threadC和threadD的目標(biāo)對(duì)象是a2。threadA和threadB共享a1的成員number,而threadC和threadD共享a2的成員number。在這種情況下threadA,和threadB共用一個(gè)全局變量number,;threadC和threadD相同。classTargetObjectimplementsRunnable{privateintnumber=0;publicvoidsetNumber(intn){number=n;}publicvoidrun(){while(true){if(Thread.currentThread().getName().equals("add")){number++;例子5中共有4個(gè)線程:threadA、threa29System.out.printf("%d\n",number);}if(Thread.currentThread().getName().equals("sub")){number--;System.out.printf("%12d\n",number);}try{Thread.sleep(1000);}catch(InterruptedExceptione){}}}}System.out.prin30publicclassExample8_5{publicstaticvoidmain(Stringargs[]){ThreadthreadA,threadB,threadC,threadD;TargetObjecta1=newTargetObject(),//線程的目標(biāo)對(duì)象a2=newTargetObject();threadA=newThread(a1);//目標(biāo)對(duì)象是a1的線程threadB=newThread(a1);a1.setNumber(10);threadA.setName("add");threadB.setName("add");threadC=newThread(a2);//目標(biāo)對(duì)象是a2的線程publicclassExample8_5{31threadD=newThread(a2);a2.setNumber(-10);threadC.setName("sub");threadD.setName("sub");threadA.start();threadB.start();threadC.start();threadD.start();}}

threadD=newThread(a2)32

2.關(guān)于run方法中的局部變量對(duì)于具有相同目標(biāo)對(duì)象的線程,當(dāng)其中一個(gè)線程享用CPU資源時(shí),目標(biāo)對(duì)象自動(dòng)調(diào)用接口中的run方法,當(dāng)輪到另一個(gè)線程享用CPU資源時(shí),目標(biāo)對(duì)象會(huì)再次調(diào)用接口中的run方法,。不同線程的run方法中的局部變量互不干擾,一個(gè)線程改變了自己的run方法中局部變量的值不會(huì)影響其他線程的run方法中的局部變量??蠢?返回2.關(guān)于run方法中的局部變量返回33publicclassExample8_6{publicstaticvoidmain(Stringargs[]){Strings1="張三",s2="Jam.keven";Movemove=newMove(s1,s2);Threadzhang,keven;zhang=newThread(move);//共享目標(biāo)對(duì)象keven=newThread(move);zhang.setName(s1);keven.setName(s2);zhang.start();keven.start();}}publicclassExample8_6{34classMoveimplementsRunnable{Strings1,s2;Move(Strings1,Strings2){this.s1=s1;this.s2=s2;}publicvoidrun(){inti=0;while(true){classMoveimplementsRunnable35if(Thread.currentThread().getName().equals(s1)){i=i+1;System.out.println(s1+"線程的局部變量:"+i);if(i>=4){System.out.println(s1+"線程進(jìn)入死亡狀態(tài)");if(Thread.currentTh36return;}}elseif(Thread.currentThread().getName().equals(s2)){i=i-1;System.out.println(s2+"線程的局部變量:"+i);return;37if(i<=-4){System.out.println(s2+"線程進(jìn)入死亡狀態(tài)");return;}}try{Thread.sleep(800);}catch(InterruptedExceptione){}}}}if(i<=-4){388.6線程的常用方法

1.start()

線程調(diào)用該方法將啟動(dòng)線程,使之從新建狀態(tài)進(jìn)入就緒隊(duì)列排隊(duì),一旦輪到它來享用CPU資源時(shí),就可以脫離創(chuàng)建它的主線程獨(dú)立開始自己的生命周期了。2.run()Thread類的run()方法與Runnable接口中的run()方法的功能和作用相同,都用來定義線程對(duì)象被調(diào)度之后所執(zhí)行的操作,都是系統(tǒng)自動(dòng)調(diào)用而用戶程序不得引用的方法。8.6線程的常用方法1.start()39

3.sleep(intmillsecond)優(yōu)先級(jí)高的線程可以在它的run()方法中調(diào)用sleep方法來使自己放棄處理器資源,休眠一段時(shí)間。3.sleep(intmillsecond)40

4.isAlive()在線程的run方法結(jié)束之前,即沒有進(jìn)入死亡狀態(tài)之前,線程調(diào)用isAlive()方法返回true,當(dāng)線程進(jìn)入死亡狀態(tài)后(實(shí)體內(nèi)存被釋放),線程仍可以調(diào)用方法isAlive(),這時(shí)返回的值是false。(線程未調(diào)用start方法之前,調(diào)用isAlive()方法返回false).需要注意的是,一個(gè)已經(jīng)運(yùn)行的線程在沒有進(jìn)入死亡狀態(tài)時(shí),不要再給線程分配實(shí)體,由于線程只能引用最后分配的實(shí)體,先前的實(shí)體就會(huì)成為“垃圾”,并且不會(huì)被垃圾收集機(jī)收集掉。4.isAlive()41

現(xiàn)在讓我們看一個(gè)例子,在下面的例子7中一個(gè)線程每隔1秒鐘在命令行窗口輸出機(jī)器的當(dāng)前時(shí)間,在輸出3秒之后,該線程又被分配了實(shí)體,新實(shí)體又開始運(yùn)行。這時(shí),我們?cè)诿钚忻棵腌娔芸匆妰尚挟?dāng)前時(shí)間,因?yàn)槔鴮?shí)體仍然在工作。publicclassExample8_7{publicstaticvoidmain(Stringargs[]){Aa=newA();a.thread.start();}}現(xiàn)在讓我們看一個(gè)例子,在下面的例子7中一個(gè)線程每隔142classAimplementsRunnable{Threadthread;intn=0;A(){thread=newThread(this);}publicvoidrun(){classAimplementsRunnable{43while(true){n++;

System.out.println(newjava.util.Date());try{Thread.sleep(1000);}catch(InterruptedExceptione){}if(n==3){while(true){44thread=newThread(this);thread.start();}}}}

thread=newThread(this455.currentThread()currentThread()方法是Thread類中的類方法,可以用類名調(diào)用,該方法返回當(dāng)前正在使用CPU資源的線程。6.interrupt()intertupt方法經(jīng)常用來“吵醒”休眠的線程。當(dāng)一些線程調(diào)用sleep方法處于休眠狀態(tài)時(shí),一個(gè)使用CPU資源的其它線程在執(zhí)行過程中,可以讓休眠的線程分別調(diào)用interrupt方法“吵醒”自己,即導(dǎo)致休眠的線程發(fā)生InterruptedException異常,從而結(jié)束休眠,重新排隊(duì)等待CPU資源。

5.currentThread()46在下面的例子8中,有3個(gè)線程:zhangXiao、zhengMing和teacher,其中2個(gè)線程:zhangXiao和zhengMing準(zhǔn)備休眠10秒鐘后,再分別輸出“早上好!”和“goodmorning!”。teacher線程在輸出3句“上課”后,“吵醒”休眠的線程:zhangXiao和zhengMing。

publicclassExample8_8{publicstaticvoidmain(Stringargs[]){ClassRoomroom=newClassRoom();room.zhangXiao.start();返回在下面的例子8中,有3個(gè)線程:zhangXiao47room.zhengMing.start();room.teacher.start();}}classClassRoomimplementsRunnable{ThreadzhangXiao,zhengMing,teacher;返回room.zhengMing.start()48ClassRoom(){teacher=newThread(this);zhangXiao=newThread(this);zhangXiao.setName("張小");zhengMing=newThread(this);zhengMing.setName("鄭明");teacher.setName("劉老師");}publicvoidrun(){Threadthread=Thread.currentThread();if(thread==zhangXiao||thread==zhengMing){返回ClassRoom(){返回49try{System.out.println(thread.getName()+"休息10秒后再說問候");Thread.sleep(10000);}catch(InterruptedExceptione){System.out.println(thread.getName()+"被吵醒了");返回try{返回50}System.out.println(thread.getName()+"說:早上好!");}elseif(thread==teacher){for(inti=1;i<=3;i++){System.out.println(thread.getName()+"說:\t上課!");返回}返回51

try{Thread.sleep(500);}catch(InterruptedExceptione){}}zhengMerrupt();//吵醒zhengMingzhangXerrupt();//吵醒zhangXiao}}}返回try{返回528.7線程同步兩個(gè)或多個(gè)線程訪問同一個(gè)變量時(shí)就要注意同步問題:

線程同步是指多個(gè)線程要執(zhí)行一個(gè)synchronized修飾的方法,如果一個(gè)線程A占有CPU資源期間,使用的synchronized方法正在被調(diào)用執(zhí)行,那么在該synchronized方法返回之前,即synchronized方法調(diào)用執(zhí)行完畢之前,其他占有CPU資源的線程一旦調(diào)用這個(gè)synchronized方法就會(huì)引起堵塞,堵塞的線程要一直等到堵塞的原因消除(synchronized方法返回),再排隊(duì)等待CPU資源,以便使用這個(gè)同步方法。8.7線程同步兩個(gè)或多個(gè)線程訪問同一個(gè)變量時(shí)就要53

在下面的例子9中有兩個(gè)線程:accountant和cashier,他倆共同擁有一個(gè)帳本。他倆都可以使用saveOrTake(intnumber)對(duì)帳本進(jìn)行訪問,會(huì)計(jì)使用saveOrTake方法時(shí),向帳本上寫入存錢記錄;出納使用saveOrTake方法時(shí),向帳本寫入取錢記錄。因此,當(dāng)會(huì)計(jì)正在使用saveOrTake方法時(shí),出納被禁止使用,反之也是這樣。publicclassExample8_9{publicstaticvoidmain(Stringargs[]){StringaccountantName="會(huì)計(jì)",cashierName="出納";Bankbank=newBank(accountantName,cashierName);返回在下面的例子9中有兩個(gè)線程:accountant和ca54Threadaccountant,cashier;accountant=newThread(bank);cashier=newThread(bank);accountant.setName(accountantName);cashier.setName(cashierName);accountant.start();cashier.start();}}返回Threadaccountant,cash55classBankimplementsRunnable{intmoney=300;StringaccountantName,cashierName;publicBank(Strings1,Strings2){accountantName=s1;cashierName=s2;}publicvoidrun(){saveOrTake(30);//線程占有CPU資源期間調(diào)用了同步方法}返回classBankimplementsRunnable56publicsynchronizedvoidsaveOrTake(intnumber){//同步方法if(Thread.currentThread().getName().equals(accountantName)){for(inti=1;i<=3;i++){money=money+number;返回publicsynchronizedvoidsaveO57try{Thread.sleep(1000);//存入30萬稍歇一下,出納仍不能使用該方法}catch(InterruptedExceptione){}System.out.println("我是"+accountantName+"目前帳上有"+money+"萬");}}返回try{Thread.sleep(100058elseif(Thread.currentThread().getName().equals(cashierName)){for(inti=1;i<=2;i++){money=money-number/2;try{Thread.sleep(1000);}catch(InterruptedExceptione){}System.out.println("我是"+cashierName+"目前帳上有"+money+"萬");}}}}

返回elseif(Thread.current598.8在同步方法中使用wait()、notify和notifyall

wait()、notify()和notifyAll()都是Object類中的final方法,被所有的類繼承,且不允許重寫的方法。當(dāng)一個(gè)線程使用的同步方法中用到某個(gè)變量,而此變量又需要其它線程修改后才能符合本線程的需要,那么可以在同步方法中使用wait()方法。使用wait()方法可以中斷方法的執(zhí)行,使本線程等待,暫時(shí)讓出CPU的使用權(quán),并允許其它線程使用這個(gè)同步方法。其它線程如果在使用這個(gè)同步方法時(shí)不需要等待,那么它使用完這個(gè)同步方法的同時(shí),應(yīng)當(dāng)用notifyAll()方法通知所有的由于使用這個(gè)同步方法而處于等待的線程結(jié)束等待。

8.8在同步方法中使用wait()、notify和not60

在下面的例子10中,模擬3個(gè)人排隊(duì)買票,每人買1張票。售票員只有1張五元的錢,電影票五元錢一張。張某拿1張二十元的人民幣排在孫某前面買票,孫某拿1張十元的人民幣排在趙的前面買票,趙某拿1張五元的人民幣排在最后。那么,最終的賣票次序應(yīng)當(dāng)是孫、趙、張。publicclassExample8_10{publicstaticvoidmain(Stringargs[]){Strings1="張三",s2="孫大名",s3="趙中堂";Cinemacanema=newCinema(s1,s2,s3);Threadzhang,sun,zhao;

返回在下面的例子10中,模擬3個(gè)人排隊(duì)買票,每61

zhang=newThread(canema);sun=newThread(canema);zhao=newThread(canema);zhang.setName(s1);sun.setName(s2);zhao.setName(s3);zhang.start();sun.start();zhao.start();}}classCinemaimplementsRunnable{//實(shí)現(xiàn)Runnable接口的類(電影院)TicketSellerseller;//電影院的售票員返回zhang=newThread(canem62Stringname1,name2,name3;//買票人的名字(線程的名字)Cinema(Strings1,Strings2,Strings3){seller=newTicketSeller();name1=s1;name2=s2;name3=s3;}publicvoidrun(){if(Thread.currentThread().getName().equals(name1)){返回Stringname1,name2,name3;63seller.sellTicket(20);}elseif(Thread.currentThread().getName().equals(name2)){seller.sellTicket(10);}elseif(Thread.currentThread().getName().equals(name3)){seller.sellTicket(5);}}}返回seller.sellTicket(2064classTicketSeller{//負(fù)責(zé)賣票的類intfiveNumber=1,tenNumber=0,twentyNumber=0;publicsynchronizedvoidsellTicket(intreceiveMoney){Strings=Thread.currentThread().getName();if(receiveMoney==5){fiveNumber=fiveNumber+1;System.out.println(s+"給售票員5元錢,售票員賣給"+s+"一張票,不必找贖");}elseif(receiveMoney==10){返回classTicketSeller{//負(fù)責(zé)65while(fiveNumber<1){try{System.out.println(s+"給售票員10元錢");System.out.println("售票員請(qǐng)"+s+"靠邊等一會(huì)");wait();//如果線程占有CPU期間執(zhí)行了wait(),就進(jìn)入中斷狀態(tài)System.out.println(s+"結(jié)束等待,繼續(xù)買票");}catch(InterruptedExceptione){}}fiveNumber=fiveNumber-1;tenNumber=tenNumber+1;返回while(fiveNumber<1)66System.out.println(s+"給售票員10元錢,售票員賣給"+s+"一張票,找贖5元");}elseif(receiveMoney==20){while(fiveNumber<1||tenNumber<1){try{System.out.println(s+"給售票員20元錢");System.out.println("售票員請(qǐng)"+s+"靠邊等一會(huì)");wait();//如果線程占有CPU期間執(zhí)行了wait(),就進(jìn)入中斷狀態(tài)System.out.println(s+"結(jié)束等待,繼續(xù)買票");}catch(InterruptedExceptione){}返回System.out.println(67}fiveNumber=fiveNumber-1;tenNumber=tenNumber-1;twentyNumber=twentyNumber+1;System.out.println(s+"給售票員20元錢,售票員賣給"+s+"一張票,找贖15元");}notifyAll();}}

返回}返回688.9掛起、恢復(fù)和終止線程掛起:有時(shí)候兩個(gè)線程,不涉及需要調(diào)用同一個(gè)同步方法,但線程也可能需要臨時(shí)停止,即所謂掛起一個(gè)線程,就是暫時(shí)讓出CPU的使用權(quán),暫時(shí)停止執(zhí)行,但停止的時(shí)間不確定,不能使用sleep方法,停止線程。線程如果有目標(biāo)對(duì)象,那么當(dāng)前線程在占用CPU期間,只要調(diào)用wait()方法流可以掛起一個(gè)線程。為了恢復(fù)這個(gè)掛起的線程,其它線程在占有CPU資源期間讓掛起線程的目標(biāo)對(duì)象在同步方法中調(diào)用notifyAll();方法即可。8.9掛起、恢復(fù)和終止線程掛起:有時(shí)候兩個(gè)線程,不涉及需698.9掛起、恢復(fù)和終止線程線程如果沒有目標(biāo)對(duì)象(用thread子類創(chuàng)建的線程)那么當(dāng)前線程在占用CPU期間,只要在某個(gè)同步方法中調(diào)用wait()方法就可以掛起一個(gè)線程?;謴?fù)線程只需調(diào)用notifyAll()即可。終止線程只需結(jié)束run()方法即可。8.9掛起、恢復(fù)和終止線程線程如果沒有目標(biāo)對(duì)象(用thr708.9掛起、恢復(fù)和終止線程在下面的例子11中,線程thread每隔一秒鐘輸出一個(gè)整數(shù),輸出3個(gè)整數(shù)后,該線程掛起;主線程負(fù)責(zé)恢復(fù)thread線程繼續(xù)執(zhí)行。publicclassExample8_11{publicstaticvoidmain(Stringargs[]){Atarget=newA();//線程thread的目標(biāo)對(duì)象Threadthread=newThread(target);8.9掛起、恢復(fù)和終止線程在下面的例子11中,線程thr718.9掛起、恢復(fù)和終止線程thread.setName("張三");thread.start();while(target.getStop()==false){}System.out.println("我是主線程,負(fù)責(zé)恢復(fù)"+thread.getName()+"線程");target.restart();//恢復(fù)thread線程}}8.9掛起、恢復(fù)和終止線程thread.728.9掛起、恢復(fù)和終止線程classAimplementsRunnable{intnumber=0;booleanstop=false;booleangetStop(){8.9掛起、恢復(fù)和終止線程738.9掛起、恢復(fù)和終止線程returnstop;}publicvoidrun(){while(true){number++;System.out.println(Thread.currentThread().getName()+"的number="+number);8.9掛起、恢復(fù)和終止線程ret748.9掛起、恢復(fù)和終止線程if(number==3){try{System.out.println(Thread.currentThread().getName()+"被掛起");stop=true;hangUP();//掛起線程System.out.println(Thread.currentThread().getName()+"恢復(fù)執(zhí)行");8.9掛起、恢復(fù)和終止線程if(758.9掛起、恢復(fù)和終止線程}catch(Exceptione){}}try{Thread.sleep(1000);}catch(Exceptione){}8.9掛起、恢復(fù)和終止線程768.9掛起、恢復(fù)和終止線程}}publicsynchronizedvoidhangUP()throwsInterruptedException{wait();}8.9掛起、恢復(fù)和終止線程}778.9掛起、恢復(fù)和終止線程publicsynchronizedvoidrestart(){notifyAll();}}對(duì)于用Thread的子類創(chuàng)建的對(duì)象的線程:面的例子12中,thread是用Thread的子類創(chuàng)建的對(duì)象,每隔一秒鐘輸出一個(gè)整數(shù),輸出3個(gè)整數(shù)后,該線程掛起。主線程負(fù)責(zé)恢復(fù)thread線程繼續(xù)執(zhí)行。8.9掛起、恢復(fù)和終止線程publicsync78publicclassExample8_12{publicstaticvoidmain(Stringargs[]){MyThreadthread=newMyThread();thread.setName("張三");thread.start();while(thread.getStop()==false){}System.out.println("我是主線程,負(fù)責(zé)恢復(fù)"+thread.getName()+"線程");thread.restart();//恢復(fù)thread線程}}publicclassExample8_12{79classMyThreadextendsThread{intnumber=0;booleanstop=false;booleangetStop(){returnstop;}publicvoidrun(){while(true){number++;System.out.println(Thread.currentThread().getName()+"的number="+number);classMyThreadextendsThread{80if(number==3){try{System.out.println(Thread.currentThread().getName()+"被掛起");stop=true;hangUP();//掛起線程System.out.println(Thread.currentThread().getName()+"恢復(fù)執(zhí)行");}catch(Exceptione){}}try{Thread.sleep(1000);}if(number==3){81catch(Exceptione){}}}publicsynchronizedvoidhangUP()throwsInterruptedException{wait();}publicsynchronizedvoidrestart(){notifyAll();}}catch(Exceptione){828.10線程聯(lián)合一個(gè)線程A在占有CPU資源期間,可以讓其它線程調(diào)用join()和本線程聯(lián)合,如:B.join();我們稱A在運(yùn)行期間聯(lián)合了B。如果線程A在占有CPU資源期間一旦聯(lián)合B線程,那么A線程將立刻中斷執(zhí)行,一直等到它聯(lián)合的線程B執(zhí)行完畢,A線程再重新排隊(duì)等待CPU資源,以便恢復(fù)執(zhí)行。如果A準(zhǔn)備聯(lián)合的B線程已經(jīng)結(jié)束,那么B.join()不會(huì)產(chǎn)生任何效果。在下面的例子13中,一個(gè)線程在運(yùn)行期間聯(lián)合了另外一個(gè)線程。返回8.10線程聯(lián)合一個(gè)線程A在占有CPU資源83publicclassExample8_13{publicstaticvoidmain(Stringargs[]){JoinThreada=newJoinThread();a.threadA.start();}}classJoinThreadimplementsRunnable{ThreadthreadA,threadB;Stringcontent[]={"今天晚上,","大家不要","回去的太早,","還有工作","需要大家做!"};publicclassExample8_13{84JoinThread(){threadA=newThread(this);threadB=newThread(this);threadB.setName("經(jīng)理");}publicvoidrun(){if(Thread.currentThread()==threadA){System.out.println("我等"+threadB.getName()+"說完再說話");threadB.start();JoinThread(){85while(threadB.isAlive()==false){}try{threadB.join();//線程threadA開始等待threadB結(jié)束}catch(InterruptedExceptione){}System.out.printf("\n我開始說話:\"我明白你的意思了,謝謝\"");}elseif(Thread.currentThread()==threadB){System.out.println(threadB.getName()+"說:");for(inti=0;i<content.length;i++){while(threadB.isAl86System.out.print(content[i]);try{threadB.sleep(1000);}catch(InterruptedExceptione){}}}}}System.out.prin878.10守護(hù)線程

一個(gè)線程調(diào)用voidsetDaemon(booleanon)方法可以將自己設(shè)置成一個(gè)守護(hù)(Daemon)線程,例如:thread.setDaemon(true);線程默認(rèn)是非守護(hù)線程,非守護(hù)線程也稱作用戶(user)線程。當(dāng)程序中的所有用戶線程都已結(jié)束運(yùn)行時(shí),即使守護(hù)線程的run方法中還有需要執(zhí)行的語句,守護(hù)線程也立刻結(jié)束運(yùn)行。一般地,用守護(hù)線程做一些不是很嚴(yán)格的工作,線程的隨時(shí)結(jié)束不會(huì)產(chǎn)生什么不良的后果。一個(gè)線程必須在運(yùn)行之前設(shè)置自己是否是守護(hù)線程。下面的例子14中有一個(gè)守護(hù)線程返回8.10守護(hù)線程一個(gè)線程調(diào)用返回88classDaemonimplementsRunnable{ThreadA,B;Daemon(){A=newThread(this);B=newThread(this);}publicvoidrun(){if(Thread.currentThread()==A){classDaemonimplementsRunnab89for(inti=0;i<8;i++){System.out.println("i="+i);try{Thread.sleep(1000);}catch(InterruptedExceptione){}}}elseif(Thread.currentThread()==B){while(true){for(inti=0;i<8;i+90System.out.println("線程B是守護(hù)線程");try{Thread.sleep(1000);}catch(InterruptedExceptione){}}}}}System.out.prin91publicclassExample8_14{publicstaticvoidmain(Stringargs[]){Daemona=newDaemon();a.A.start();a.B.setDaemon(true);a.B.start();}}publicclassExample8_14{92第八章問答題1.線程和進(jìn)程是什么關(guān)系?答:線程是比進(jìn)程更小的執(zhí)行單位。一個(gè)進(jìn)程在其執(zhí)行過程中,可以產(chǎn)生多個(gè)線程,形成多條執(zhí)行線索,每條線索,即每個(gè)線程也有它自身的產(chǎn)生、存在和消亡的過程,也是一個(gè)動(dòng)態(tài)的概念。第八章問答題1.線程和進(jìn)程是什么關(guān)系?答:線程是比進(jìn)93第八章問答題2.

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(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)論