計(jì)算機(jī)課件 線程_第1頁
計(jì)算機(jī)課件 線程_第2頁
計(jì)算機(jī)課件 線程_第3頁
計(jì)算機(jī)課件 線程_第4頁
計(jì)算機(jī)課件 線程_第5頁
已閱讀5頁,還剩58頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

第12章線程

[內(nèi)容提要]

在網(wǎng)絡(luò)分布式編程盛行的今天,多線程編程已成

為現(xiàn)代編程語言普遍具備的功能。

Java作為一門網(wǎng)絡(luò)語言,從內(nèi)核全面支持多線程

技術(shù)。

多線程是Java程序設(shè)計(jì)的特色之一,利用多線程

技術(shù)可以方便地實(shí)現(xiàn)任務(wù)的并發(fā)處理。

本章結(jié)合代碼講解了線程相關(guān)的基本概念,并通

過幾個(gè)實(shí)例重點(diǎn)展示Java線程的構(gòu)造、調(diào)度和

數(shù)據(jù)交換的方法。

1

第1節(jié)、線程的概念

一、線程的概念

在多線程程序設(shè)計(jì)中,線程是程序執(zhí)行過程中一個(gè)

子運(yùn)行序列。

進(jìn)程與程序的關(guān)系

程序是靜態(tài)的代碼,一個(gè)進(jìn)程是某個(gè)程序的一次執(zhí)

行過程

例如你的機(jī)器只裝了一個(gè)QQ程序,但是同時(shí)允許兩個(gè)帳號(hào)登

陸,那此時(shí)有兩個(gè)QQ進(jìn)程運(yùn)行

進(jìn)程與線程的關(guān)系

線程則是一個(gè)比進(jìn)程更細(xì)微的程序執(zhí)行序列,是進(jìn)

程的某個(gè)子序列

例如你上QQ后可以給甲傳文件,給乙視頻聊天。那此時(shí)該QQ

進(jìn)程管理了兩個(gè)線程。分別負(fù)責(zé)傳文件和視頻聊天2

進(jìn)程與線程的關(guān)系

由誰管理?

線程由程序負(fù)責(zé)管理,而進(jìn)程由操作系統(tǒng)調(diào)度。

線程依附于進(jìn)程

線程依附于進(jìn)程的上下文環(huán)境中,隨進(jìn)程或父線

程執(zhí)行啟啟動(dòng)。

地址空間

多個(gè)線程使用相同的地址空間,因此線程之間的

通信非常方便。

而進(jìn)程之間使用不同的地址空間,可以單獨(dú)執(zhí)行。

3

多線程程序設(shè)計(jì)是并發(fā)程序設(shè)計(jì)的一種,

各個(gè)線程之間是并行執(zhí)行的,當(dāng)計(jì)算機(jī)

只有一個(gè)CPU時(shí),操作系統(tǒng)會(huì)使用分時(shí)

或其他方法來模擬并行運(yùn)行的效果。

舉例:如可以同時(shí)聽歌和玩游戲,是操作系統(tǒng)把時(shí)間段

分成很小的片斷在兩個(gè)運(yùn)行的程序間切換

多線程程序設(shè)計(jì)應(yīng)用非常廣泛。

當(dāng)我們需要在一個(gè)程序當(dāng)中同時(shí)執(zhí)行幾段

代碼時(shí),就需要用到多線程來實(shí)現(xiàn)。

舉例:網(wǎng)站下載,大型科學(xué)計(jì)算等。

4

第2節(jié)線程的創(chuàng)建

第一種方法

以多線程方式啟動(dòng)執(zhí)行的類必須繼承

javaJang.Thread類,實(shí)現(xiàn)該類的Run()方法,

然后控制線程的執(zhí)行;

第二種方法

如果只要一段代碼在單獨(dú)線程中運(yùn)行

則可以繼承java.lang.Runnable接口,并I尋該段

代碼放在該接口Run()方法中,然后通過構(gòu)造

Thread類對(duì)象實(shí)現(xiàn)線程的建立和運(yùn)行控制。

5

一、Thread類創(chuàng)建多線程應(yīng)用程序

Thread類提供了用于啟動(dòng)、掛起、恢復(fù)以及終止

線程的一系列方法,

除此之外,還提供了控制線程優(yōu)先級(jí)以及線程的

名字等其他方面的方法。

6

使用Thread類建造多線程程序的方法

1.將自定義的應(yīng)用程序類繼承Thread類,并覆蓋其nin()

方3,當(dāng)線程開始啟動(dòng)時(shí)會(huì)調(diào)用這個(gè)方法。

2.通過覆蓋nm()方法,就可以使線程在后臺(tái)完成一些有

用的任務(wù)。

3.然后,在自定義類中的main()方法,聲明一個(gè)Thread類

型的對(duì)象使之指向自定義應(yīng)用程序類的實(shí)例。

如:Threadtl=newExtendThreadDemo(l);

4.再調(diào)用start。方法啟動(dòng)線程,該方法會(huì)調(diào)用前面定義的

run()方法,并使之脫離main()方法的主線程,在操作

系統(tǒng)中申請(qǐng)新的線程運(yùn)行。

如:啟動(dòng)線程

7

publicclassExtendThreadDeiaoextends包是自動(dòng)引入的

2{〃繼承Thread類

3;intthreadNumber;

4publicExtendThreadDemo(intnum)〃線程類構(gòu)造函數(shù)

5{ithreadNujiiber^uni;-j,;'u

bpublicvoidr呵)〃線程體

7{System.out.printIn("Thiead"+threadNuiaber+'isrunning");

8try{Thread.sleep(5000);//1H<B^5000MS

Q}

10catch(InterruptedExceptionie){}

11Systeni.out.printIn(threadNuikber+"isfinished");

12}

13publicstati(cvoidiaain(Stringargs[]){

14System.out.printIn("Creatingthread1");

15Threadtl=nevExtendThreadDew(1),〃創(chuàng)建線卷實(shí)例

lbSystem.outprintIn("Cieatmgthiead2");

17Threadt2=nevExtendThreadDemo(2);

18tl2s.tasrttart(FT^自動(dòng)執(zhí)行線程

19

20}}勺run()方迅一

輸出如下:

產(chǎn)生

Creatingthread1publicstaticvoidmain(Stringargs[]){

MaiCreatingthread2System.out.pnntlnf"Creatingthread1");

s

ThreadlisrunningThreadtlnevExtendT加疝em。⑴;〃創(chuàng)建線松炙例

ThreSystemout.println(',Creatirigthread2');

ThreThread!isrunningThreadt2=nevExtendThreadDeiao(2);

約過5秒,繼續(xù)顯示tl.start

Thre1isflnishedt2.start));

Threid;2isflnished

同時(shí)有三個(gè)線程在運(yùn)行,CPU同時(shí)執(zhí)行,請(qǐng)分析每句輸

出是哪個(gè)線程產(chǎn)生的?

ThreadlThreadl

publicvoid叫)磔腳publicvoid叫)7雌#

《嫻加加岫(1+threadhber+"iiriming1);御既眥血岫(加的14喇岫4#施加);

聞眥壯$1部伽);/咻昭岫stry(隔,嫡)麻M)哪蹦*

}}

catchflnterruptedExceptionie){)catchflntemptedExceptionie){}

5ysteiout.piinth(threadhber+"isfinis劇);■M胤毗p血珈仙闌!岫對(duì)isf誦副);

注意,一般創(chuàng)建的普通線程(也稱用戶線程)均

會(huì)在完成自身的工作之后才會(huì)終止,main方京

構(gòu)成的線程稱為主線程,它總是用戶線程,故

它總是要等它創(chuàng)建的子線程結(jié)束后才能結(jié)束。

與用戶線程相對(duì)的是守護(hù)線程,只有當(dāng)其它線程

(如實(shí)際的應(yīng)用程序)正在運(yùn)行時(shí)才有用,一

旦其他線程結(jié)束后,不管它本身的任務(wù)是否完

全完成均會(huì)終止。

10

publicstaticvoidmain(Stringargs[]){

System.out.printin(^CreatingHiread1”);

Threadtl=newExtendThreadDemo(l);

Sysrem.out.prmtln(Creatingthread”);

Threadt2=new

將線程ti設(shè)為守護(hù)線

tl.setDaemon(tn比方程

執(zhí)行結(jié)果:

t2.setDaemon(true);

CreatingThreadl

tl.start();t2.start();CreatingThread2

try(Threadlisrunning

Tliread.sleep(1000);Thread2isrunning

注:程序一秒后自動(dòng)退出

修改后的主程序在線程tl,t2開始后一秒后就自動(dòng)退出,由于線程“工

是守護(hù)線程,所以當(dāng)主程序退出時(shí),他們也被中止,所以君不到

輸出“1isflnished2is行nished”

二、使用Rimnable接口創(chuàng)建多線程應(yīng)用程序

由于Java只支持單重繼承,使用擴(kuò)展Thread類的

方式實(shí)現(xiàn)多線程,就會(huì)導(dǎo)致應(yīng)用程序不能繼承

其他的類,在構(gòu)造復(fù)雜程序時(shí)很不方便。

使程序能夠多線程執(zhí)行的更好的方法是實(shí)現(xiàn)

java.lang.Runnable接口。

Runnable接口唯一的定義了一個(gè)mn()方法,實(shí)

現(xiàn)該接口必須實(shí)現(xiàn)這個(gè)方法。

應(yīng)用程序類實(shí)現(xiàn)了這個(gè)接口則該類既可以以多線

程的方式執(zhí)行,又可以繼承其他的類。

12

步驟

1.創(chuàng)建這類多線程程序的方法是定義類時(shí)聲明實(shí)現(xiàn)

Runnable接口并寫好線程體run()方法,

2.然后在main()方法中聲明并創(chuàng)建Runnable類型的對(duì)

象,以該對(duì)彖為構(gòu)造參數(shù),聲明并構(gòu)造Thread對(duì)象,

3.當(dāng)調(diào)用了線程對(duì)象的star")方法時(shí),新創(chuàng)建的線程就

會(huì)調(diào)用run()方法。

4.run()方法結(jié)束時(shí),線程便停止。

注意,同一個(gè)Runnable對(duì)象可以被傳遞給多個(gè)線程,所

以幾個(gè)并發(fā)的線程可以使用相同的代碼,并操作相

同的數(shù)據(jù)。

使用Runnable接口實(shí)現(xiàn)多線程程序還有一個(gè)好處可是使

用Runnable實(shí)例作構(gòu)造參數(shù)創(chuàng)建多個(gè)Thread實(shí)例比

創(chuàng)建多個(gè)經(jīng)過擴(kuò)展的Thread實(shí)例開銷要小。

下例實(shí)現(xiàn)了類似例12?1的交替顯示字符串的功能。

13

publicclassRunnab1eThreadDemoimpleinentsRunnabl己//java.iang包是自動(dòng)弓I人的

{//繼承Runnable接口

publicvoidrun()〃定義線程體

Systein.out.printIn("IainaninstanceofthejavalanqRunnableinter!ace"

}.

publicstaticvoidmain(Stringargs[]){

System.out.printIn("Creatingrunnableobject");

Runnablerun=newRunnabl巳口1時(shí)1北回1。(),//創(chuàng)建線程接口實(shí)例

System.out.printIn("Creatingfirstthread");

Threadtl=newThread(run);“儂據(jù)線程接口實(shí)例創(chuàng)建線程實(shí)例

System.out.printIn("CreatingsecondthreadM;

Threadt2snewThread(run);

System.out.printin("Startingbothtlnedd^"),

ti.start()*...

t2..start0;〃啟動(dòng)線程

Creatingrunnableobject

Creatingfirstthread例12-3運(yùn)行結(jié)果

Creatingsecondthread

Startingboththreads//書上漏寫了!

Iamaninstanceofthejava.lang.Runnableinterface14

Iamaninstanceofthejava.lang.Runnableinterface

publicclassAppletThreadextendsjava.applet.AppletimplementsRunnable

繼承Runnable:接口TXlI工口.CA

Threadt;inti=22;1Z-4

Stringoutput=frTlireadisrunningpublicvoidstart()

{〃在Appl"的主方祛中創(chuàng)建.啟動(dòng)新線程

if(t==null){t=newThreadfthis);t.start();

})

publicvoidstop(){運(yùn)行效果:

if(t!=null)t.stop();Applet窗口中不停的往下出現(xiàn)

t=null;Threadisrunning的字符串。

)當(dāng)改變窗口大小時(shí),以前的字

publicvoidrun(){〃/程體符全消失,重新開始在新位置

while(true){開始畫該字符串

output=rTTlireadisrunning

repaint():

try{Thread,sleep(1000);線程休眠10口0秒

catch(InterruptedExceptione){}

}}.......,..

publicvoidupdate(Graphicsg){〃羽蛾刷新屏幕方法,身加式繪制

paint(g);)

publicvoidpaint(Graphicsg){〃頁載屏幕繪制方法

i=i+10;

g.drauString(output,100/i);〃在指定,位置畫出字符串。utput,

))

第3節(jié)線程的生存周期

每個(gè)線程從生成實(shí)例獲得資源到運(yùn)行結(jié)束、釋放

空間要經(jīng)歷三種基本的狀態(tài)。

線程實(shí)例在內(nèi)存中創(chuàng)建后線程處于就緒狀態(tài),然

后在父線程中通過調(diào)用Thread類的start。方法

來啟動(dòng)線程體nin()方法。

線程進(jìn)入亍狀不,當(dāng)線程因需要的某個(gè)資源被

其他線程占用等原因而進(jìn)入尚待狀態(tài)。

線程體執(zhí)行完畢或被父線程終止時(shí),線程結(jié)束。

Java提供了很多方法可以對(duì)線程的生存周期進(jìn)

行各種控制和調(diào)度,以靈活實(shí)現(xiàn)程序的功能。

16

一、線程的優(yōu)先級(jí)

在支持多線程的系統(tǒng)中,多線程是通過在線程之

間次速切換來模擬代碼的并發(fā)執(zhí)行而實(shí)現(xiàn)的。

由于操作系統(tǒng)使用單個(gè)線程不能控制的任意算法

在線程之間進(jìn)行切換,線程的執(zhí)行順序、運(yùn)行

時(shí)間和調(diào)度線程的時(shí)間是無法預(yù)測的。

為了加強(qiáng)對(duì)線程的調(diào)度,最簡單的方式是設(shè)定線

程的相對(duì)優(yōu)先級(jí),指示操作系統(tǒng)哪個(gè)線程更重

---------------------------------------

17

1設(shè)置線程優(yōu)先級(jí)

在Java中,用數(shù)字1.10指明了線程的優(yōu)先級(jí),其中,

10是最高優(yōu)先級(jí),1是最低優(yōu)先級(jí)。

較高優(yōu)先級(jí)的線程嚼搶占較低優(yōu)先級(jí)的線程的

CPU資源。

優(yōu)先級(jí)調(diào)度在不同的操作系統(tǒng)中執(zhí)行效果可能不

同。

有的操作系統(tǒng)會(huì)分配較多的運(yùn)行時(shí)間給高優(yōu)先級(jí)

的線程。

有的操作系統(tǒng)中,高優(yōu)先級(jí)的線程可迫使低優(yōu)先

級(jí)的線程掛起,回到優(yōu)先級(jí)隊(duì)列中等待,直到

正在運(yùn)行的線程主動(dòng)暫停運(yùn)行。

18

表12-1線程優(yōu)先級(jí)

常量值

Thread.MAXPRIORITY10

Thread.NORM_PRIORITY5

Thread.MIN_PRIORITY1

使用setPriority()方法可以設(shè)置線程的優(yōu)先級(jí)。

注意,可以在啟動(dòng)線程之前設(shè)置線程的優(yōu)先級(jí),

也可以在線程運(yùn)行過程中改變線程的優(yōu)先級(jí)別。

19

importjava.io.*;

classmytiireadlextendsThread{

publicvoidrun(){

while(true)例程12-5

Sysr.eiu.out.println("Timeadlrr);

1)二二.一:

classuiYtiireadSextendsThread(

publicvoidrun(){

wHile(true)

System,out.printin(rrThxead2r,);

})

classmainclass(

publicstaticvoidmain(Stringargs[]){

mytiireadltl=newmytlireadl();

mytiiread2C2=newmythreadZ();

tl.setPriority(7):

t2.setPrioricy(3);

try{Syst-em.out,.prmdn(rr

Systern.in.read();存在問題:線程和

}—:」一―一tlt2

都沒有被啟動(dòng)!

catcli(lOExceptione)(Syst

tl.stop();匕2?stop();改進(jìn)方案見下頁

1)

改進(jìn)后的程序12?5

1importjava.io.*;

classmythreadlextendsThread{31classMinclass{

3privateintnum=0,3:publicstaticvoidmainfStnngargs[]){

4publicvoidrun(){

5try(Bnythreadltl:n即叫thread#;

6while(true){Niaythread2t2=nevmyth忸d2();

7System.out.printIn("Thread1n);

8num=num+1;工tl.setPrionty(lO);

9}3bt2,setPnority(l);

10}catch(Excep11one){}

11}3?tl.start(),

12publicvoiddestory(){38t2,start));

13System.outprintIn("Thread1"+num);

1439try(

15}4。System,out.printlnj"pleaseinputachar");

lb}

17classmythread2extendsThread{41Systeiin.readt);

18privateintnum=0;12}

19publicvoidrun(){

20trv{4』catch(Exceptione){System,out.pnntln(e);}

21while(true){

22System.out.printIn("Thiead2');

23num=num+1;45tl.stopO;

24)46t2,stop();

25}catch(Exceptione){}Af*JI.八

26)執(zhí)行效果:

27publicvoiddestory(){

28System.out.println("Thread2

29}輸入字母按回車,程序結(jié)束后,可

30以看到線程11和12的具體執(zhí)行次數(shù)!

2獲得線程的優(yōu)先級(jí)

線程體中可以通過調(diào)用getPriority。方法確定當(dāng)

前線程的優(yōu)先級(jí)。

若優(yōu)先級(jí)不夠高,則可以調(diào)用設(shè)置優(yōu)先級(jí)方法進(jìn)

行調(diào)整。

該方法返回一個(gè)整數(shù),表明線程的優(yōu)先級(jí)。

例如下面的代碼段可以獲取當(dāng)前正在運(yùn)行的線程

的優(yōu)先級(jí):

Threadt=Thread.currentThread();

System.out.println(6Triority:w+t.getPriority());

22

二、線程的控制方法

1.線程休眠sleep。

在線程運(yùn)行時(shí),可以主動(dòng)調(diào)用靜態(tài)方法

Thread,sleep。方法讓線程休眠一段時(shí)間。

1)此時(shí)線程讓出CPU,進(jìn)入就緒隊(duì)列。讓低優(yōu)

先級(jí)的線程運(yùn)行,以提高程序的整體效率。

2)運(yùn)用此方法還可以實(shí)現(xiàn)延時(shí)效果。

2.打斷線程休眠intermpt。

在主線程中通過調(diào)用intermpt。方法可以使進(jìn)入

休眠狀態(tài)的子線程提前喚醒。

例程12?6演示了在主線程中將長時(shí)間休眠的子線程

sleepy在用戶敲入回車后提前喚醒的過程。

publicclassSleepyHeadextendsThread(

publicvoidrun(){例程126

System,out.printin(frIfeelsleepy.Wake配ineighthours,5k邙begin.r,);

try(

Thread.sleep(1000*60*60*8);

5ysteia.out.println(Sleepend.Tliarwasanicenaprr);

}

catch(InterruptedExceptionie){

System.err.printin("Justfivemoreniinute3....r,);

})

publicstaticvoidmain(Stringargs[])throwsjava.io.I0Exception(

Hireadsleepynew

sleepy.start();該程序執(zhí)行過程如下:

System.out.printin("PressIfeelsleepy.Wakemeineighthours

System,in.read()昭順而向Pressentertointerruptthethread

sleepy,interrupt!);〃打斷裁敲入回車

})Justfivemoreminutes.......

3?停止/銷毀線程stop()/destroy()

在主線程中使用靜態(tài)方法stop。可以結(jié)束子線程。調(diào)

用時(shí)要求主線程擁有被控刷線程的對(duì)象名(引用)

4.掛起/恢復(fù)線程suspend()/resume()

在主線程中調(diào)用Thread.suspenQO方法和

Thread,resume。方法可以暫停和恢復(fù)線程的運(yùn)行。

5.主動(dòng)讓出CPUyield()

Thread.yield()線程調(diào)用該方法后,即進(jìn)入就緒隊(duì)

歹心等存下一o次競爭CPU重新運(yùn)行。

6.等待別的線程結(jié)束join。

有時(shí)一個(gè)運(yùn)行到某個(gè)時(shí)候,必須等待另外一個(gè)線程

結(jié)束后才可以繼續(xù)運(yùn)行,可以調(diào)用join。方法。

publicclassUaitThreadextendsTliread{

publicvoidrun])//線程體

(

System,out.printin(r,uait5second....,r);

try{例程12-7

Thread,sleep(5000);〃休眠;5杪

)

catch(InterruptedExceptionie){}

)

publicstaticvoidmain(Stringargs[])tlirowsjava.lang.Int.erruptedException

Threadu=newWaitThread();

%);〃線程開始

System,out.printin(rrBegmtoWaitendofTliread?/attout.5seconds.*');

%join();〃等待,直到線程dying結(jié)束

System.out.printin(r,Tliread棺hasdied.");

輸出:

BegintoWaitendofThreadw,about5seconds

Wait5second...

Threadwhasdied

26

第4節(jié)線程的同步控制

一、線程間通信概述圖

同一程序的多個(gè)線程之間經(jīng)常要互相傳遞數(shù)據(jù)或

共同訪問相同的數(shù)據(jù)。在線程之間可以有很多

方樂實(shí)現(xiàn)數(shù)據(jù)的交換。

最簡單的是通過內(nèi)類,即把線程類定義成應(yīng)用程

序類的內(nèi)類,這樣線程就可以共享訪問應(yīng)用程

序類居鼠員變量數(shù)據(jù)。

第二種方法是通過管道流套接實(shí)現(xiàn)一個(gè)線程的輸

人作為另一個(gè)線程的彳俞出。

27

第4節(jié)線程的同步控制

最為常見的一種方法是通過構(gòu)造器傳遞,

具體做法:

修需要共同訪問的數(shù)據(jù)定義成一個(gè)類,每個(gè)線程類的構(gòu)

造器設(shè)置一個(gè)參數(shù)用以接受共享數(shù)據(jù)類對(duì)象,為此每

個(gè)線程需定義一個(gè)共享數(shù)據(jù)類型的成員變量。

優(yōu)點(diǎn):可以把對(duì)共同訪問數(shù)據(jù)的一些操作方法封裝到共享

數(shù)據(jù)類中,結(jié)構(gòu)清晰,符合面向?qū)ο蟪绦蛟O(shè)計(jì)的思想

要求。

例:、“警察抓小偷”程序通過構(gòu)造器實(shí)現(xiàn)線程之間數(shù)據(jù)交

流的方法。

28

如圖12-1所示,警察A沿X軸正

方向巡邏,小偷B沿Y軸方向y

逃跑,企圖越過警察的封鎖。

Java程序模擬這個(gè)場景,可以

在程序中設(shè)計(jì)兩個(gè)線程類:A_____

Police類和Thief類,分別執(zhí)

g

行巡邏和逃跑的過程。

圖12-1

29

classPosition(〃警索和小諭坐標(biāo)校置類,兩級(jí)程拄享訪問

intx,y;

publicvoidshow(){〃顯示警察和小愉位置,兩線,程抖親訪問

System,out.printin(rrPolicein"+x)》工1工口1.,?

SysismoujprinEi("Thiefin”+y);例木王12?o8:ptdemo.java

publicbooleancatxhed()(〃判斷警察是否可以抓住小愉

if(x>-3&&x<3&&y>-3&&Y<3){return(true);)

elsereturn(false);

})

publicclasspcdemo{〃主程序炎

publicstaticvoidmain(Stringargs[]){

ptdemot=newptdemo();t.go();

)

publicvoidgo(){

Positionpos=newPosition。;〃產(chǎn)生慢置類實(shí)例

Policep=newPolice(pos);

Threadpt=newThizead(p);〃力生警察線程實(shí)例

Thiefth=newThief(pos);

Threadtht=newT}iread(th);〃產(chǎn)生小偷線程實(shí)例

tht.start();〃后動(dòng)小偷

pt.start();〃啟動(dòng)警察30

))

classPoliceimplementsRnnnat>le{

Positionp;

publicPolice(Positionpp){〃構(gòu)造函數(shù)

P=PP;}

publicvoidrun(){〃線,程體例程

while(true){12-8:ptdemo.java

£or(intx=T00;x<=100;x++){//警察位置循環(huán)移動(dòng)

p.x=x;〃修改位置對(duì)森

p.show();〃顯示當(dāng)前位皙狀態(tài)

if(p.catched()){

System,out.printin(r,CarchedI!r,);

System.exit.(O);〃苔抓住了小偷.則提示"Catched”并退出本線程.

}))11

classThiefimplementsRunnable]

Positionp;

publicThief(Positionpp){〃構(gòu)造函數(shù)

P=PP;

)

publicvoidrun()(〃線程體

while(true){

for(int丫=-100;丫〈=100;丫++)(〃小偷循環(huán)侈動(dòng)

p.Y=Y;〃耳人.位置對(duì)■教

p.show();//顯示當(dāng)前也置狀態(tài)

if(p.catched()){

Systern.out.println(°Ihasbeencatched,soIstophere111lr);

Systeii.exit(CI);〃若被—?.*抓住,則提示“beencatched”并給束本毆程.

}}}}}

二、線程的同步

1.線程同步與線程安全

從上面的代碼中,我們會(huì)發(fā)現(xiàn)警察和小偷的位置可能不

是交錯(cuò)輸出,而是連續(xù)輸出兩個(gè)警察的位置后再連續(xù)

輸出兩個(gè)小隔的彳立置。如:

Policein-6

Policein-6

Thiefin-4

Thiefin-4

而我們預(yù)想的輸出應(yīng)該是:

Policein-6

Thiefin-5

Policein-6

Thiefin-4

32

classPoliceimpleiiientsRunnable{

Positionp;

classPosition(〃警蔡和小洞生?標(biāo)位置燙,的續(xù)程共孕厲問

publicPolice(Positionpp){//

intXM

P=PP;)

publicvoid3hQ?(M〃顯示警察和小偷慢租,兩線程共享力問

publicvoidrun(){〃線程體

弁舒tamnut.DrintlrLfrrFoiiCEin

while(true){

for(intx=-100;x<=100;x-H-Systeiii.out.printin(Lefin"+Y);

p.x=x;〃修改位置對(duì)象

口,ShouH://顯示當(dāng)前位置狀態(tài)

if(p.catched()){Policein-6

System,out.printin(rrCatched!!rr);

System.exit(O);〃若抓住了小偷,則提示"Cashed"并.退出本線程.

}}})}

classThiefimpleiiientsRunnable!

Positionp;Policein-6

publicThief(Positionpp){〃構(gòu)選函數(shù)

P=PP;

classPosition!tP英,的續(xù)程共談E間

publicvoidrun(){/7線程體intx〃;

while(true){publicvoid顯金和小麗慢置,兩線程共享后問

for(inty=-100;y<=100;y++){//<停7耳七51nut,printin廠“口]icmin'』丫十

P.Y=Y;〃寫入位置對(duì)象Systeiii.out.println£rrTliiefinrr+y);

P.3hOM();〃顯示當(dāng)前位置狀態(tài)

if(p.catched()){

System.out.printin(rrIhasbeencatched,soIstophere!!!rr);

Systuni.exit(O);〃若被抓住,則提示"beencatched"并結(jié)束本線程.33

}}}}}

當(dāng)幾個(gè)線程使用同一個(gè)對(duì)象(變量)時(shí),這種執(zhí)行的

步調(diào)不一致不僅會(huì)導(dǎo)致意外的輸出順序,還會(huì)

帶來讀臟數(shù)據(jù),寫入丟失等嚴(yán)重問題

如果在線程體中對(duì)一些訪問共享數(shù)據(jù)或者不能打

斷順序執(zhí)行完整性的代碼進(jìn)行斥控告,

就可以使各個(gè)線程以固定秩序訪問共享數(shù)據(jù),

從而避免這些問題。

這種考慮了多線程競爭資源訪問沖突的代碼或程

序稱為線程安全的。

Java中線程同步有兩種機(jī)制:方法級(jí)同步和代碼

級(jí)同步。

34

2?方法級(jí)同步

定義:指共享對(duì)象的方法在定義時(shí)就聲明為同步

的,即需要事先申請(qǐng)到同步鎖才能執(zhí)行。

作用:防止兩個(gè)線程在同'一時(shí)刻對(duì)同一■對(duì)象執(zhí)行

方法。

原理:

當(dāng)調(diào)用某共享對(duì)象的同步方法時(shí),線程取得對(duì)象

鎖(或稱為對(duì)象監(jiān)視器),

當(dāng)其他線程試圖執(zhí)行該對(duì)象的同步方法時(shí),將會(huì)

發(fā)現(xiàn)對(duì)彖被鎖住了,鹵而進(jìn)入掛起n大態(tài),直

到前面的線程結(jié)束同步方法,釋放對(duì)象鎖。

35

聲明共享對(duì)象的同步方法的語法是直接在類的定義

申在方法定義之前加上synchronized美鍵字。

如:

classPosition{

intx,y;

publicsynchronizedvoidshow(){〃力口上

synchronized關(guān)鍵字

System.out.println(uPolicein“+x);

System.out.println(uThiefin“+y);

}

publicBooleancatched(){

if(x>-3&&x<3&&y>-3&&y<3){

return(true);

}

elsereturn(false);

36

)逢奸就可以達(dá)到同步輸出的效果了。

3.代碼級(jí)同步

代碼級(jí)同步有兩種形式,

1)在共享對(duì)象的方法中聲明申請(qǐng)同步鎖、

2)在線程體的調(diào)用代碼中標(biāo)明申請(qǐng)同步鎖。

37

1)在共享對(duì)象的方法中聲明申請(qǐng)同步鎖

(1)定義共享對(duì)象時(shí),還可以只對(duì)其成員方法

中的某段代碼進(jìn)行同步控制。

這樣的同步代碼段在被多個(gè)線程調(diào)用時(shí)的執(zhí)行機(jī)

制是和方法級(jí)同步類似的。

聲明共享對(duì)象方法的代碼塊同步的語法如下:

synchronized(Objecto)

{〃被同步的代碼塊

38

要使例程12-8能夠?qū)olice和Thief的位置交替同

步輸由,除了對(duì)Position類的show。方法進(jìn)行同

步夕卜還可以直接對(duì)該方法的輸出語句塊進(jìn)行

同步,而程序的其他地方不必修改。

修show。方法的定義修改如下:

publicvoidshow(){

synchronized(this){

〃力口上synchronized關(guān)鍵字,

〃this代表同步共享對(duì)象即調(diào)用該方法的對(duì)象實(shí)例。

System.out.println(6Tolicein"+x);

System.out.println(^Thiefin"+y);

2)在線程體的調(diào)用代碼中標(biāo)明申請(qǐng)同步鎖

如果共享對(duì)象的方法沒有定義為方法級(jí)同步或代碼塊同

步,而且多線程共享訪問的對(duì)象方法定義不能修改,

則可以對(duì)調(diào)用該對(duì)象方法的調(diào)用代碼塊進(jìn)行同步修飾。

其語法格式就是代碼塊同步的格式,需要在synchronize

關(guān)鍵字之后指定同步共享對(duì)象的對(duì)象名。

使例程12-8對(duì)Police和Thief的位置交替同步輸出的第三種

修改方法是在Police和Thief類的p.show()語句次最:

synchronized(p){

〃在同步對(duì)象方法的調(diào)用語句前加代碼塊同步控制,

〃口為同步訪問的對(duì)象的對(duì)象名

p?show();

40

比較三種同步控制方式

1)方法級(jí)同步的聲明最簡單明了。我們把方法聲明中考

慮了方法同步的共享類資源稱為線程安全的。

2)設(shè)計(jì)共享資源類時(shí)采用代碼塊級(jí)同步時(shí)也比較簡單。

但方法級(jí)同步方式有一個(gè)好處是在方法原型中可以看

出資源是否被設(shè)計(jì)成了線程安全的。

3)對(duì)調(diào)用該對(duì)象方法的調(diào)用代碼塊進(jìn)行同步:比較麻煩,

需要在每個(gè)需要調(diào)用共享資源方法的線程體中加上這

種修飾。

例程12?9:方法級(jí)同步

程序功能:三個(gè)并發(fā)線程依次給共享計(jì)數(shù)器加10最后輸

出計(jì)數(shù)器Counter的結(jié)果。

41

publicclassCountingThreadimplementsRunnable

〃計(jì)數(shù)Runnabl2鏤口:使線程安全的計(jì)數(shù)整?Counter喀加指定次

(CountermyCouncer;

mtcountAmoimt;

publicCountingThread(Countercounter,mtamount)

{myCouncer=counter;count.Amount.=amount;

publicvoidrun()

(〃格計(jì)數(shù)躇增加指定的次激例程12?9

for(inti=1;i<=countAmount;14-+-)

{//增加計(jì)數(shù)

myCountereaseC

publicstaticvoidmain(Stringargs[])throwsExcept.ion

{//創(chuàng)建線程安全的計(jì)數(shù)(Counter、的實(shí)例I(共享對(duì)象)

Counterc=newCounter();

〃創(chuàng)建使線程安全計(jì)數(shù)器為的接口tCountingThread)的實(shí)仲I

Runnablerunner=newCountingThread(c,10);

“用CountingThread實(shí)例創(chuàng)建多個(gè)線程

System,out.print.ln(rrStartingcountingthreads,r);

Threadtl=newThread(runner);

Threadr2=newThread(runner);

Thread匕3=newThread(ruimer);

tl.start();V2.start();t3.start();

〃等待所有線程結(jié)束

tl.join();Z2.join();t3.join();

〃讀取共享美中的最后結(jié)果并顯示_____________

System,out.print.ln('Towit.erv-hlueisr,+Cc\getCoimt(j^>

))

publicclassCounter

〃線程安全的計(jì)數(shù)整奏(共享焚〕

{privateintcountValue;

publicCounter()

{countValue=0;}

publicCounter(mtstart)例程12?9:

{countValue=start;}

々同掃法:噌理數(shù)

publieCg^ichronize^>voidincreaseCount()

{intcount=countValue;

try

{Thread.sleep(5);)

catch(InterrupzedExcepzionie){}

count=count+1;輸出:

countValue=count;

Startingcountingthreads

)

〃同步分虹造值_C_o__u_n__terualueis30

publie^SichronigetCount()

returncountValue;}

43

}

例程12?10:代碼塊級(jí)同步

程序功能:六個(gè)并發(fā)線程依次給共享計(jì)數(shù)器

SynchBlockcouter加1紿共享數(shù)據(jù)緩沖區(qū)

i^ynchBlockbuffer追力口字符串。最后輸出緩沖

區(qū)buffer的結(jié)果為

11211231121123411211231121123456

若不加同步時(shí)僅顯示為:

123456

44

publicstaticvoidmain(Strmaargs[])throwsExceptic

{//創(chuàng)建線程實(shí)例

SynchBlockblock=newSynchBlock();

Threadtl=newThread(block);

例程12-10Threadz2=newThread(block);

Threadt3=newThread(block);

Threadt4=newThread(block);

Threadt5=newThread(block);tl.start();

源代碼t5.start();Tlireadt6=newThread(block);

Lt6.start();

t2.start();t.3.start();t4.start();

〃等待三個(gè)線程結(jié)束

tl.join();t5.join();t6.join();t2.join.();

publicclassSynchBlockimplementsRunnableM);

(StrmgBufferbuffer;intcounter;i(block.buffer);

publicSynchBlock()

{buffer=newStringBuffer();

counter=1;

I

publicvoidrun()

)〃代一碼塊同先

.步

{synchronized(buf<司7rmr

5>rr

口^rr

{System.out.p|?i

<司r

5rm

inttempVari]口rg

步j(luò)

<司rr

〃增加要放,5T-r

口^Tlr

Stringmessa)m緒

<.司Trr

5步r

message=mess口^T:r

?緒!

^7r步

司r

步>rr

try口^rM

?

溫馨提示

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