




版權(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- T-ZZB Q071-2024 酶底物法微生物智能培養(yǎng)計(jì)數(shù)一體機(jī)
- T-ZJHIA 16-2024 特殊醫(yī)學(xué)用途配方食品臨床營養(yǎng)治療營養(yǎng)篩查數(shù)據(jù)集
- 二零二五年度離婚協(xié)議中夫妻共同財(cái)產(chǎn)清算補(bǔ)充協(xié)議
- 二零二五年度直播帶貨主播合作權(quán)益保障合同
- 2025年度智能制造合作伙伴協(xié)議書
- 二零二五年度木制家具生產(chǎn)廠木工用工協(xié)議書
- 二零二五年度車輛掛靠運(yùn)輸合同車輛運(yùn)輸合同安全保障協(xié)議
- 二零二五年度個(gè)人租賃帶太陽能熱水系統(tǒng)住宅合同
- 二零二五年度餐飲行業(yè)知識(shí)產(chǎn)權(quán)保護(hù)協(xié)議
- 二零二五年度兼職攝影師聘用合同模板
- 家校共育之道
- DeepSeek入門寶典培訓(xùn)課件
- 西安2025年陜西西安音樂學(xué)院專職輔導(dǎo)員招聘2人筆試歷年參考題庫附帶答案詳解
- 《作文中間技巧》課件
- 廣東省2025年中考物理仿真模擬卷(深圳)附答案
- 2025屆八省聯(lián)考 新高考適應(yīng)性聯(lián)考英語試題(原卷版)
- 新蘇教版一年級(jí)下冊數(shù)學(xué)第1單元第3課時(shí)《8、7加幾》作業(yè)
- 2024年山東電力高等??茖W(xué)校高職單招職業(yè)技能測驗(yàn)歷年參考題庫(頻考版)含答案解析
- 2024年電力交易員(高級(jí)工)職業(yè)鑒定理論考試題庫(單選題、多選題、判斷題)
- 《平面廣告賞析》課件
- 【公開課】同一直線上二力的合成+課件+2024-2025學(xué)年+人教版(2024)初中物理八年級(jí)下冊+
評(píng)論
0/150
提交評(píng)論