第6章java的多線程_第1頁
第6章java的多線程_第2頁
第6章java的多線程_第3頁
第6章java的多線程_第4頁
第6章java的多線程_第5頁
已閱讀5頁,還剩93頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

第12章

程第12章

程主要內(nèi)容線程的概念線程的生命周期Java中多線程的編程繼承Thread類與使用Runnable接口Thread類的主要方法線程的同步與死鎖第12章

程基本概念之一:進(jìn)程進(jìn)程是正在運(yùn)行的一個(gè)程序程序:靜態(tài)對象--進(jìn)程:動(dòng)態(tài)過程操作系統(tǒng)為每個(gè)進(jìn)程分配一段內(nèi)存空間,包括:代碼、數(shù)據(jù)以及堆棧等資源多任務(wù)的操作系統(tǒng)(OS)中,進(jìn)程切換對CPU資源消耗較大第12章

程基本概念之二:多線程單線程多線程進(jìn)程傳統(tǒng)進(jìn)程多線程進(jìn)程第12章

程基本概念之二:多線程線程是比進(jìn)程更小一級的執(zhí)行單元線程不能獨(dú)立存在,必須存在于進(jìn)程中,各線程間共享進(jìn)程空間的數(shù)據(jù)線程創(chuàng)建、銷毀和切換的負(fù)荷遠(yuǎn)小于進(jìn)程,又稱為輕量級進(jìn)程(lightweightprocess)。第12章

程線程的概念程序是一段靜態(tài)的代碼,它是應(yīng)用程序執(zhí)行的藍(lán)本一個(gè)進(jìn)程既包括其所要執(zhí)行的指令,也包括了執(zhí)行指令所需的任何系統(tǒng)資源,如CPU、內(nèi)存空間、I/O端口等,不同進(jìn)程所占用的系統(tǒng)資源相對獨(dú)立線程是進(jìn)程執(zhí)行過程中產(chǎn)生的多條執(zhí)行線索,是比進(jìn)程單位更小的執(zhí)行單位第12章

程線程的結(jié)構(gòu)CPUCodeData虛擬CPU,封裝在

java.lang.Thread類中,它控制著整個(gè)線程的運(yùn)行執(zhí)行的代碼,傳遞給Thread類,由

Thread類控制順序執(zhí)行處理的數(shù)據(jù),傳遞給

Thread類,是在代碼執(zhí)行過程中所要處理的數(shù)據(jù)第12章

程線程與進(jìn)程線程在形式上同進(jìn)程十分相似—都是用一個(gè)順序執(zhí)行的語句序列來完成特定的功能不同之處:線程沒有入口,也沒有出口,因此其自身不能自動(dòng)運(yùn)行,而必須棲身于某一進(jìn)程之中,由進(jìn)程觸發(fā)執(zhí)行在系統(tǒng)資源的使用上,屬于同一進(jìn)程的所有線程共享該進(jìn)程的系統(tǒng)資源,但是線程之間切換的速度比進(jìn)程切換要快得多第12章

程進(jìn)程與線程的區(qū)別文件輸入輸出裝置各種系統(tǒng)資源數(shù)據(jù)區(qū)段程序區(qū)段只有一個(gè)地方在執(zhí)行文件輸入輸出裝置各種系統(tǒng)資源數(shù)據(jù)區(qū)段程序區(qū)段同時(shí)有數(shù)個(gè)地方在執(zhí)行傳統(tǒng)的進(jìn)程多線程的任務(wù)第12章

程多線程的優(yōu)勢多線程編程簡單,效率高。使用多線程可以在線程間直接共享數(shù)據(jù)和資源,而多進(jìn)程之間不能做到這一點(diǎn)適合于開發(fā)服務(wù)程序–如Web服務(wù)、聊天服務(wù)等適合于開發(fā)有多種交互接口的程序–如聊天程序的客戶端、網(wǎng)絡(luò)下載工具適合于有人機(jī)交互又有計(jì)算量的程序–如字處理程序Word、Excel等第12章

程線程的調(diào)度調(diào)度策略–時(shí)間片–搶占式:高優(yōu)先級的線程搶占CPUJava的調(diào)度方法同優(yōu)先級線程組成先進(jìn)先出隊(duì)列,使用時(shí)間片策略對高優(yōu)先級,使用優(yōu)先調(diào)度的搶占式策略12第12章

程線程的狀態(tài)Java的線程是通過Java的軟件包java.lang中定義的類

Thread來實(shí)現(xiàn)的–當(dāng)生成一個(gè)Thread類的對象之后,就產(chǎn)生了一個(gè)線程。通過該對象實(shí)例,可以啟動(dòng)線程、終止線程,或者暫時(shí)掛起線程等Thread類本身只是線程的虛擬CPU,線程所執(zhí)行的代碼是通過方法run()

來完成的,方法run()稱為線程體–在一個(gè)線程被建立并初始化以后,Java的運(yùn)行時(shí)系統(tǒng)就自動(dòng)調(diào)用run()方法,正是通過run()方法才使得建立線程的目的得以實(shí)現(xiàn)第12章

程線程的狀態(tài)線程一共有四種狀態(tài):新建(new)、可運(yùn)行狀態(tài)(runnable)、死亡(dead)及堵塞(blocked)new

Thread()創(chuàng)建新線程可運(yùn)行態(tài)start()不可運(yùn)行態(tài)stop()stop()死亡yield()stop()run()exitsuspend()sleep()wait()I/O流阻塞resume()notify()/notifyAll()I/O指令等待睡眠掛起阻塞就緒運(yùn)行第12章

程線程的生命周期(續(xù))Newborn:線程已創(chuàng)建,但尚未執(zhí)行Runnable:(就緒)線程已被調(diào)度,按優(yōu)先級和先到先服務(wù)原則在隊(duì)列中排隊(duì)等待CPU時(shí)間片資源Runnnig:正在運(yùn)行Blocked:(阻塞)因某事件或睡眠而被暫時(shí)性地掛起Dead:正常/強(qiáng)行中斷,退出運(yùn)行狀態(tài)第12章

程12.1 Java中的多線程實(shí)現(xiàn)技術(shù)12.1.1

線程的生命周期每個(gè)Java程序都有一個(gè)缺省的主線程,對于Application,主線程是main()方法執(zhí)行的線索。對于Applet,主線程指揮瀏覽器加載并執(zhí)行Java小程序。要想實(shí)現(xiàn)多線程,必須在主線程中創(chuàng)建新的線程對象。Java語言使用Thread類及其子類的對象來表示線程。新建的線程在它的一個(gè)完整的生命周期中通常要經(jīng)歷新生、就緒、運(yùn)行、阻塞和死亡等五種狀態(tài),這五種狀態(tài)之間的轉(zhuǎn)換關(guān)系和轉(zhuǎn)換條件如圖12.1所示。第12章

程等待睡眠掛起阻塞死亡圖12.1

線程的生命周期新建start就緒運(yùn)行第12章

程1.新生狀態(tài)當(dāng)用new關(guān)鍵字和某線程類的構(gòu)造方法創(chuàng)建一個(gè)線程對象后,這個(gè)線程對象處于新生狀態(tài),此時(shí)它已經(jīng)有了相應(yīng)的內(nèi)存空間,并已被初始化。處于該狀態(tài)的線程可通過調(diào)用start()方法進(jìn)入就緒狀態(tài)。第12章

程2.就緒狀態(tài)處于就緒狀態(tài)的線程已經(jīng)具備了運(yùn)行的條件,但尚未分配到CPU資源,因而它將進(jìn)入線程隊(duì)列排隊(duì),等待系統(tǒng)為它分配

CPU。一旦獲得CPU資源,則該線程就進(jìn)入運(yùn)行狀態(tài),并自動(dòng)地調(diào)用自己的run方法。此時(shí),它脫離創(chuàng)建它的主線程,獨(dú)立開始了自己的生命周期。第12章

程3.運(yùn)行狀態(tài)進(jìn)入運(yùn)行狀態(tài)的線程執(zhí)行自己的run方法中的代碼。若遇到下列情況之一,則將終止run方法的執(zhí)行。終止操作。調(diào)用當(dāng)前線程的stop方法或destroy方法進(jìn)入死亡狀態(tài)。等待操作。調(diào)用當(dāng)前線程的join(millis)方法或wait(millis)方法進(jìn)入阻塞狀態(tài)。當(dāng)線程進(jìn)入阻塞狀態(tài)時(shí),在millis毫秒內(nèi)可由其他線程調(diào)用notify或notifyAll方法將其喚醒,進(jìn)入就緒狀態(tài)。在millis毫秒內(nèi)若不喚醒則須等待到當(dāng)前線程結(jié)束。第12章多線程睡眠操作。調(diào)用sleep(millis)方法來實(shí)現(xiàn)。當(dāng)前線程停止執(zhí)行后,則處于阻塞狀態(tài),睡眠millis毫秒之后重新進(jìn)入就緒狀態(tài)。掛起操作。通過調(diào)用suspend方法來實(shí)現(xiàn)。將當(dāng)前線程掛起,進(jìn)入阻塞狀態(tài),之后當(dāng)其他線程調(diào)用當(dāng)前線程的resume方法后,才能使其進(jìn)入就緒狀態(tài)。退讓操作。通過調(diào)用yield方法來實(shí)現(xiàn)。當(dāng)前線程放棄執(zhí)行,進(jìn)入就緒狀態(tài)。當(dāng)前線程要求I/O時(shí),則進(jìn)入阻塞狀態(tài)。若分配給當(dāng)前線程的時(shí)間片用完時(shí),當(dāng)前線程進(jìn)入就緒狀態(tài)。如當(dāng)前線程的run方法執(zhí)行完,則線程進(jìn)入死亡狀態(tài)。第12章

程4.阻塞狀態(tài)一個(gè)正在執(zhí)行的線程在某些特殊情況下,如執(zhí)行了suspend、join或sleep方法,或等待I/O設(shè)備的使用權(quán),那么它將讓出CPU并暫時(shí)中止自己的執(zhí)行,進(jìn)入阻塞狀態(tài)。阻塞時(shí)它不能進(jìn)入就緒隊(duì)列,只有當(dāng)引起阻塞的原因被消除時(shí),線程才可以轉(zhuǎn)入就緒狀態(tài),重新進(jìn)到線程隊(duì)列中排隊(duì)等待CPU資源,以便從原來終止處開始繼續(xù)運(yùn)行。第12章

程5.死亡狀態(tài)處于死亡狀態(tài)的線程將永遠(yuǎn)不再執(zhí)行。線程死亡有兩個(gè)原因:一是正常運(yùn)行的線程完成了它的全部工作;二是線程被提前強(qiáng)制性地終止,例如,通過執(zhí)行stop或destroy方法來終止線程。第12章

程創(chuàng)建線程的方法創(chuàng)建線程的方法一——繼承Thread類–定義一個(gè)線程類,它繼承類Thread并重寫其中的方法

run()。在初始化這個(gè)類的實(shí)例時(shí),目標(biāo)對象target可以為null,表示這個(gè)實(shí)例本身具有線程體創(chuàng)建線程的方法二——實(shí)現(xiàn)Runnable接口–

Runnable是Java中用以實(shí)現(xiàn)線程的接口,從根本上講,任何實(shí)現(xiàn)線程功能的類都必須實(shí)現(xiàn)該接口實(shí)現(xiàn)Runnable接口的方式創(chuàng)建線程與用繼承Thread類的方式創(chuàng)建線程無本質(zhì)差別,但是,由于Java不支持多繼承,所以任何類如果已經(jīng)繼承了某一類時(shí),就無法再繼承Thread類,這時(shí)只能通過實(shí)現(xiàn)接口

Runnable的方式創(chuàng)建線程對象。例如,小應(yīng)用程序已經(jīng)繼承了Applet類,不能再繼承Thread類,只能通過Runnable接口實(shí)現(xiàn)多線程。第12章

程12.1.3創(chuàng)建Thread類的子類,首先是聲明子類的構(gòu)造方法,其次是用自己定義的run()方法去覆蓋Thread類的run()方法,即將自己要執(zhí)行的程序區(qū)塊寫入run()方法中。Thread類的重要方法:run()定義線程的具體操作系統(tǒng)調(diào)度此線程時(shí)自動(dòng)執(zhí)行

如何編程呢?初始時(shí)無具體操作內(nèi)容方法一通過繼承Thread類方式創(chuàng)建線程-繼承Thread類,定義run()方法第12章

程表12.1

Java.lang.Thread的構(gòu)造方法構(gòu)造方法說

明public

Thread(

)構(gòu)造一個(gè)新線程,用此方式創(chuàng)建的線程必須覆蓋run()方法public

Thread(Runnable

target)構(gòu)造一個(gè)新線程,使用指定對象target的run()方法publicThread(ThreadGroup

group,Runnable

target)在指定的線程組group中構(gòu)造一個(gè)新的線程,使用指定對象target的run()方法public

Thread(String

name)用指定字符串名name構(gòu)造一個(gè)新線程public

Thread(ThreadGroup

group,String

name)在指定的線程組group中用指定字符串名name構(gòu)造一個(gè)新線程public

Thread(Runnable

target,String

name)用指定字符串名name構(gòu)造一個(gè)新線程,使用指定對象target的run()方法public

Thread(ThreadGroup

group,Runnable

target,Stringname)在指定的線程組group中使用字符串名

name構(gòu)造一個(gè)新線程,并使用指定對象

target的run()方法第12章

程12.1.2

Thread類的方法Thread類(線程類)是java.lang包中的一個(gè)專門用來創(chuàng)建線程和對線程進(jìn)行操作的類。Java在Thread類中定義了許多方法,幫助我們運(yùn)用和處理線程。這些方法可分為四組:構(gòu)造方法。用于創(chuàng)建用戶的線程對象,表12.1列出了Thread類的構(gòu)造方法。run()方法。用于定義用戶線程所要執(zhí)行的的操作。改變線程狀態(tài)的方法。如start()、sleep()、stop()、suspend()、resume()、yield()和wait()方法等。這是最常用的一組方法。其他方法。如setPriority()、setName()等。第12章

程Thread類的有關(guān)方法start():由Newborn到Runnable啟動(dòng)線程run():線程在被調(diào)度時(shí)執(zhí)行的操作sleep(指定時(shí)間):令當(dāng)前活動(dòng)線程在指定時(shí)間段內(nèi)放棄對CPU控制,使其他線程有機(jī)會(huì)被執(zhí)行,時(shí)間到后重排隊(duì)產(chǎn)生例外InterruptedException用try塊調(diào)用sleep(),用catch塊處理例外第12章

程Thread類的有關(guān)方法(續(xù))suspend()

:掛起線程,處于阻塞狀態(tài)resume():恢復(fù)掛起的線程,重新進(jìn)入就緒隊(duì)列排隊(duì)?wèi)?yīng)用:可控制某線程的暫停與繼續(xù)方法:設(shè)一狀態(tài)變量suspendStatus=false(初始)暫停:if(!suspendStatus){T.suspend();

suspendStatus=true;}繼續(xù):if(suspendStatus){T.resume();

suspendStatus=false;}第12章

程Thread類的有關(guān)方法(續(xù))yield():對正在執(zhí)行的線程若就緒隊(duì)列中有與當(dāng)前線程同優(yōu)先級的排隊(duì)線程,則當(dāng)前線程讓出CPU控制權(quán),移到隊(duì)尾若隊(duì)列中沒有同優(yōu)先級的線程,忽略此方法stop()強(qiáng)制線程生命期結(jié)束isAlive():返回boolean,表明是否還存在第12章

程Thread類方法總結(jié)啟動(dòng)線程:start()有關(guān)線程執(zhí)行的控制:stop()、suspend()、resume()有關(guān)調(diào)度控制Thread.sleep(10);//低優(yōu)先級的線程也可以獲得執(zhí)行Thread.yield();//同優(yōu)先級的線程可以獲得執(zhí)行suspend();//暫停本線程第12章

程表12.2

Java.lang.Thread的常用方法常用方法說

明public

void

run(

)此線程的線程體,在啟動(dòng)該線程后調(diào)用此方法。可以通過使用Thread類的子類來重載此方法public

synchronized

void

start(

)啟動(dòng)線程的執(zhí)行,此方法引起run()方法的調(diào)用,調(diào)用后立即返回。如果已經(jīng)啟動(dòng)此線程,就拋出

IllegalThreadedStateException異常public

static

Thread

currentThread(

)返回當(dāng)前處于運(yùn)行狀態(tài)的Thread對象public

static

void

yield(

)使當(dāng)前執(zhí)行的Thread對象退出運(yùn)行狀態(tài),使其進(jìn)入等待隊(duì)列public

static

void

sleep(longmillis)throwsInterruptedException使當(dāng)前執(zhí)行的線程睡眠millis毫秒。如果另一個(gè)線程已經(jīng)中斷了此線程,就拋出

InterruptedException

異常第12章

程表12.2

Java.lang.Thread的常用方法常用方法說明public

static

voidsleep(longmillis,int

nanos)throws

InterruptedException使當(dāng)前執(zhí)行的線程睡眠millis毫秒和附加的nanos毫秒。如果另一個(gè)線程已經(jīng)中斷了這個(gè)線程,則拋出InterruptedException

異常public

final

void

stop(

)停止線程的執(zhí)行通過拋出對象停止線程的執(zhí)行。正常情況下,用public

final

synchronized

void戶應(yīng)該在調(diào)用stop方法時(shí)不用任何參數(shù)。但是,在stop(Throwable

o)某些特殊的環(huán)境中,通過stop方法來結(jié)束線程,并可拋出另一個(gè)對象public

void

interrupt(

)中斷一個(gè)線程第12章

程表12.2

Java.lang.Thread的常用方法public

static

boolean

interrupted(

)詢問線程是否已經(jīng)被中斷public

Boolean

isInterrupted(

)詢問另一個(gè)線程是否已經(jīng)被中斷public

void

destroy(

)消毀一個(gè)線程public

final

boolean

isAlive(

)返回表示該線程活動(dòng)狀態(tài)的boolean值public

final

void

suspend(

)掛起這個(gè)線程的執(zhí)行public

final

void

resume(

)恢復(fù)這個(gè)線程的執(zhí)行,此方法僅在使用suspend()后才有效public

final

void

setPriority(intnewPriority)設(shè)置該線程的優(yōu)先級,如果優(yōu)先級不在MIN-

PRIORITY

MAX-PRIORITY

的范圍內(nèi),就拋出IllegalArgumentException異常第12章

程表12.2

Java.lang.Thread的常用方法public

final

int

getPriority(

)獲取并返回此線程的優(yōu)先級public

final

void

setName(Stringname)設(shè)置該線程名為namepublic

final

String

getName(

)獲取并返回此線程名public

final

ThreadGroup

getThreadGroup(

)獲取并返回此線程組public

static

int

activeCount(

)返回此線程組中當(dāng)前活動(dòng)的線程數(shù)量public

static

int

enumerate(Thread

tarray[

])將此線程組中的每一個(gè)活動(dòng)線程拷貝到指定的數(shù)組tarray中,返回放到此數(shù)組中線程的數(shù)量public

final

synchronized

void

join(longmillis)throws

InterruptedException等待此線程死亡,可以指定一個(gè)以millis給出的等待時(shí)間(以毫秒為單位),如果為0則表示永遠(yuǎn)等待,如果另一個(gè)線程已經(jīng)中斷這個(gè)線程,那么就拋出Interrupted-Exception異常第12章

程public

final

synchronized

voidjoin(long

millis

,intnanos)throwsInterruptedException等待此線程死亡,可以給出millis等待時(shí)間和nanos附加時(shí)間。如果另一個(gè)線程已經(jīng)中斷此線程,則拋出InterruptedException異常public

final

void

join(

)throwsInterruptedException無限期等待此線程死亡,如果另一個(gè)線程已經(jīng)中斷此線程,則拋出InterruptedException異常public

final

boolean

isDaemon(

)返回線程是否為daemon線程public

void

check

Access(

)檢查當(dāng)前線程是否允許訪問此線程組。如果不允許,就拋出SecurityException異常public

String

toString(

)返回這個(gè)線程的字符串表示,包括該線程的名字,優(yōu)先級和線程組,覆蓋Object類中的toString方法表12.2

Java.lang.Thread的常用方法第12章

程方法一從Thread類派生出一個(gè)子類,在類中一定要實(shí)現(xiàn)run()class

Lefthand

extends

Thread

{public

void

run(){……}}然后用該類創(chuàng)建一個(gè)對象Lefthand

left

=

newLefthand();用start()方法啟動(dòng)線程

(程序11-1

11-2)left.start();第12章

程public

class

myThread

extends

Thread{public

void

run(){while(running){……

//執(zhí)行若干操作sleep(100);}}public

static

void

main(String

args[]){Thread

t

=

newmyThread();……

//執(zhí)行若干操作}}第12章

程class

Lefthand

extends

Thread{public

void

run(){for(inti=0;i<=5;i++){System.out.println("You

are

Students!");try{//sleep((int)(Math.random()

*

1000));sleep(500);}catch(InterruptedException

e){}}}}class

Righthand

extends

Thread{public

void

run(){for(inti=0;i<=5;i++){System.out.println("I

am

a

Teacher!");try{sleep(300);}catch(InterruptedException

e){}}}}第12章

程public

class

ThreadTest{static

Lefthandleft;static

Righthand

right;public

static

void

main(String[]

args){left

=

newLefthand();right

=

new

Righthand();left.start();right.start();}}第12章

程【示例程序c12_1.java】 用Thread類的子類創(chuàng)建兩個(gè)線程對象。import

java.util.*;class

c12_1

extends

Thread

{int

pauseTime;String

name;public

c12_1(inthTime,

StringhStr){pauseTime

=hTime;name

=hStr;}public

void

run(

){第12章

程Calendar

now;//Calendar是Java系統(tǒng)提供的日期時(shí)間類的類型標(biāo)識(shí)符

int

year,month,date,hour,minute,second;for(int

i=1;i<10;i++)

{try

{now=Calendar.getInstance(

);year=now.get(Calendar.YEAR);//取系統(tǒng)時(shí)間//取年值month=now.get(Calendar.MONTH)+1;

//取月值date=now.get(Calendar.DATE);

//取日期值hour=now.get(Calendar.HOUR_OF_DAY);minute=now.get(Calendar.MINUTE);second=now.get(Calendar.SECOND);//取小時(shí)值//取分值//取秒值System.out.println(""+name+"時(shí)間:"+year+"年"+month+"月"+第12章

程date+"日"+hour+"小時(shí)"+minute+"分"+second+"秒");//顯示時(shí)間

Thread.sleep(pauseTime);}catch(Exception

e){System.out.println("線程錯(cuò)誤:"+e);}}}static

public

void

main(String

args[

])

{c12_1

myThread1=new

c12_1(2000,"線程A");//A線程執(zhí)行一次后睡眠2000毫秒

myThread1.start();c12_1

myThread2=new

c12_1(1000,"線程B");//B線程執(zhí)行一次后睡眠1000毫秒

myThread2.start();}}第12章

程方法二:通過實(shí)現(xiàn)Runnable接口方式創(chuàng)建線程創(chuàng)建線程對象的另一個(gè)途徑是實(shí)現(xiàn)Runnable接口,而

Runnable接口只有一個(gè)方法run(),用戶新建線程的操作由這個(gè)方法來決定。run()方法必須由實(shí)現(xiàn)此接口的類來實(shí)現(xiàn)。定義好

run()方法之后,當(dāng)用戶程序需要建立新線程時(shí),只要以這個(gè)實(shí)現(xiàn)了run()方法的類為參數(shù)創(chuàng)建系統(tǒng)類Thread的對象,就可以把用戶實(shí)現(xiàn)的run()方法繼承過來。方法二:自定義類實(shí)現(xiàn)Runnable接口使用Thread類的另一構(gòu)造函自定義類實(shí)現(xiàn)Runnable接口數(shù):Thread(Runnable,

String)使用start()啟動(dòng)線程第12章

程例:class

A

implements

Runnable{public

void

run(){….}}class

B

{public

static

void

main(String[]

arg){Runnable

a=new

A();Thread

t=new

Thread(a);t.start();}}第12章

程【示例程序c12_2.java】 通過創(chuàng)建線程實(shí)現(xiàn)“Java

Now!”在屏幕上不停地走動(dòng)。importjava.awt.*;import

java.applet.*;public

class

c12_2

extends

Applet

implements

Runnable{Thread

th1=null;String

Message="Java

Now!"; //創(chuàng)建字符串對象Font

f=new

Font("TimesRoman",Font.BOLD,24);體對象int

x,y;public

void

init(

){//創(chuàng)建字第12章

程x=getSize(

).width; //取applet窗口的寬度,單位為像素y=getSize(

).height/2; //取applet窗口的高度,單位為像素}public

void

start(

){

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

if(th1==null){th1=new

Thread(this);th1.start(

);}}public

void

run(

){while(true){第12章

程x=x-5;if(x==0)x=getSize(

).width;repaint(); //repaint()方法調(diào)用paint()方法重畫字符串對

try{th1.sleep(500); //使th1線程睡眠500

毫秒}catch(InterruptedException

e){

}}}public

void

paint(Graphics

g){g.setFont(f);//設(shè)置字體

g.drawString(Message,x,y);}}第12章

程圖12.2

程序c12_2的運(yùn)行中的一個(gè)瞬間第12章

程模擬小球例子程序11-3是一個(gè)模擬小球平拋和自由落體的例子BallThread.java相應(yīng)的HTML文檔第12章

程import

java.awt.*;import

java.awt.event.*;import

java.applet.*;public

class

BallThread

extends

Applet

implements

Runnable{Thread

red,

blue;Graphics

redPen,

bluePen;int

t=0;public

void

init(){red

=

new

Thread(this);blue

=

new

Thread(this);redPen

=getGraphics();bluePen

=getGraphics();redPen.setColor(Color.red);bluePen.setColor(Color.blue);}public

void

start(){red.start();blue.start();}第12章public

void

run(){多

程while(true){t=t+1;if(Thread.currentThread()==red){if(t>100)t=0;redPen.clearRect(0,0,110,400);redPen.fillOval(50,(int)(1.0/2*t*9.8),15,15);//s=1/2gt2try{red.sleep(40);}catch(InterruptedException

e){}}else

if(Thread.currentThread()==blue){bluePen.clearRect(120,0,900,500);bluePen.fillOval(120+7*t,(int)(1.0/2*t*9.8),15,15);//s=v0t+1/2gt2try{blue.sleep(40);}catch(InterruptedException

e){}}}}}第12章

程兩種方法的選擇當(dāng)需要從其他類,如Applet類繼承時(shí),使用Runnable當(dāng)編寫簡單的程序時(shí),可考慮使用繼承Thread類例:RaceApplet.java具體運(yùn)行結(jié)果(線程調(diào)度)與平臺(tái)有關(guān)第12章

程public

class

Runner

extends

Thread

{

//跑者線程類public

int

tick

=

1;public

void

run()

{while

(tick

<

40000000)tick++;}}//Runner.java//RaceApplet是一個(gè)實(shí)現(xiàn)了多線程的Appletpublic

class

RaceApplet

extends

Applet

implements

Runnable

{跑線程的個(gè)數(shù)final

static

int

NUMRUNNERS=2;//定義

final

staticint

SPACING=20;//聲明兩個(gè) 跑線程Runner[]

runners

=

newRunner[NUMRUNNERS];//聲明一個(gè)畫圖線程

Thread

updateThread=null;第12章

程public

void

init(){

//重載Applet的init()方法

for(int

i=0;

i<NUMRUNNERS;i++){runners[i]

=

newRunner();

//創(chuàng)建 跑線程線程runners[i].setPriority(i+1);

//設(shè)優(yōu)先級first=1,second=2}if

(updateThread

==

null)

{//創(chuàng)建繪圖線程,并設(shè)優(yōu)先級為3updateThread

=

new

Thread(this,

"ThreadRace");updateThread.setPriority(NUMRUNNERS+1);}addMouseListener(new

MyAdapter());//注冊事件監(jiān)聽者}

//end

ofinit()第12章

程//內(nèi)部事件監(jiān)聽者類,監(jiān)聽鼠標(biāo)事件class

MyAdapter

extends

MouseAdapter

{//鼠標(biāo)點(diǎn)擊后,開始 跑及繪制線程public

void

mouseClicked(MouseEventevt)

{if

(!updateThread.isAlive())updateThread.start();//啟動(dòng)繪制線程

for(int

i=0;

i<NUMRUNNERS;i++){if

(!runners[i].isAlive())runners[i].start();

//啟動(dòng) 跑線程}}}

//end

of

class

MyAdapter第12章

程public

void

paint(Graphics

g){//paint()方法中繪制框架………}

//end

ofpaint()//update()方法中繪制賽跑者的進(jìn)度,可以消除圖畫的閃爍

public

void

update(Graphics

g){for(int

i=0;

i<NUMRUNNERS;i++){//畫兩條線

g.drawLine(SPACING,(i+1)*SPACING,SPACING

+

(runners[i].tick)/100000,

(i+1)*SPACING);}}

//end

of

update()第12章

程publicvoid

run(){//實(shí)現(xiàn)Runnable接口的run()方法

while(true){repaint();

//重新繪制,自動(dòng)調(diào)用update()方法

try{Thread.sleep(10);

//休眠,把執(zhí)行機(jī)會(huì)讓給低優(yōu)先級線程}

catch

(InterruptedException

e)

{

}}}

//end

of

run()public

void

stop(){//重載Applet的stop()方法

for(int

i=0;

i<NUMRUNNERS;i++){if

(runners[i].isAlive())runners[i]

=null;

//中止 跑線程}if

(updateThread.isAlive())

updateThread=null;

//中止繪圖線程}

//end

ofstop()}

//RaceApplet.java第12章

程線程的啟動(dòng)通過Thread類中方法start()來啟動(dòng)在程序11-3中,只要執(zhí)行:red.start();blue.start();第12章

程線程的操作方法start()啟動(dòng)線程對象;run()用來定義線程對象被調(diào)度之后所執(zhí)行的操作,用戶必須重寫run()方法;yield()強(qiáng)制終止線程的執(zhí)行;isAlive()測試當(dāng)前線程是否在活動(dòng);sleep(int

millsecond)使線程休眠一段時(shí)間,時(shí)間長短由參數(shù)所決定;void

Wait()使線程處于等待狀態(tài);第12章多

程線程的調(diào)度線程調(diào)度通常是搶占式,而不是時(shí)間片式–搶占式調(diào)度是指可能有多個(gè)線程準(zhǔn)備運(yùn)行,但只有一個(gè)在真正運(yùn)行。一個(gè)線程獲得執(zhí)行權(quán),這個(gè)線程將持續(xù)運(yùn)行下去,直到它運(yùn)行結(jié)束或因?yàn)槟撤N原因而阻塞,再或者有另一個(gè)高優(yōu)先級線程就緒,最后一種情況中稱為低優(yōu)先級線程被高優(yōu)先級線程所搶占。12.2

多線程的管理第12章

程優(yōu)先級策略優(yōu)先級高的先執(zhí)行,優(yōu)先級低的后執(zhí)行多線程系統(tǒng)會(huì)自動(dòng)為每個(gè)線程分配一個(gè)優(yōu)先級,缺省時(shí),繼承其父類的優(yōu)先級任務(wù)緊急的線程,其優(yōu)先級較高同優(yōu)先級的線程按“先進(jìn)先出”的原則第12章

程線程優(yōu)先級Thread類三個(gè)與線程優(yōu)先級有關(guān)的靜態(tài)量MAX_PRIORITY:最大優(yōu)先權(quán),值為10;MIN_PRIORITY:最小優(yōu)先權(quán),值為1;NORM_PRIORITY:默認(rèn)優(yōu)先權(quán),值為5Thread類中幾個(gè)常用的有關(guān)優(yōu)先級的方法Void

setPriority(int

newPriority)

//重置線程優(yōu)先級Int

getPriority()Static

void

yield()//獲得當(dāng)前線程的優(yōu)先級//使當(dāng)前線程放棄執(zhí)行權(quán)第12章

程【示例程序c12_3.java】 創(chuàng)建三個(gè)線程A、B、C,根據(jù)優(yōu)先級確定線程的執(zhí)行順序。class

c12_3{public

static

void

main(String

args[

]){ Thread

First=new

MyThread("A"); //創(chuàng)建A線程First.setPriority(Thread.MIN_PRIORITY);

//A線程優(yōu)先級為1Thread

Second=newMyThread("B"); //創(chuàng)建B線程Second.setPriority(Thread.NORM_PRIORITY+1); //B線程優(yōu)先級為6Thread

Third=new

MyThread("C"); //創(chuàng)建C線程第12章

程Third.setPriority(Thread.MAX_PRIORITY);//C線程優(yōu)先級為10First.start();Third.start();Second.start(

);}}class

MyThread

extends

Thread{String

message;MyThread(String

message){

this.message=

message;}public

void

run(

){

for(inti=0;i<2;i++)System.out.println(message+"

"+getPriority(

));}}第12章

程該程序的運(yùn)行結(jié)果如下:C

10

C

10

B

6

B

6

A

1

A

1從程序的運(yùn)行結(jié)果中可以看出,雖然線程A在程序中最先調(diào)用start()方法進(jìn)入就緒狀態(tài),但由于它的優(yōu)先級是三個(gè)線程中最低的,所以最后才得以執(zhí)行。第12章

程線程的調(diào)度被阻塞的線程按次序排列,組成一個(gè)阻塞隊(duì)列。所有就緒但沒有運(yùn)行的線程則根據(jù)其優(yōu)先級排入一個(gè)就緒隊(duì)列CPU空閑時(shí),如果就緒隊(duì)列不空,隊(duì)列中第一個(gè)具有最高優(yōu)先級的線程將運(yùn)行當(dāng)一個(gè)線程被搶占而停止運(yùn)行時(shí),它的運(yùn)行態(tài)被改變并放到就緒隊(duì)列的隊(duì)尾;同樣,一個(gè)被阻塞(可能因?yàn)樗呋虻却齀/O設(shè)備)的線程就緒后通常也放到就緒隊(duì)列的隊(duì)尾為保證給其他線程留有執(zhí)行的機(jī)會(huì),可以通過間隔地調(diào)用sleep()第12章

程線程的調(diào)度例例11-4public

class

xyz

implements

Runnable{public

void

run(){while(true){……

//執(zhí)行若干操作//給其他線程運(yùn)行的機(jī)會(huì)try{Thread.sleep(10);}catch(InterruptedException

e){//該線程為其他線程所中斷}}}}第12章

程sleep()是靜態(tài)方法,可以通過Thread.sleep(x)直接引用–

x指定了線程在再次啟動(dòng)前必須休眠的最小時(shí)間,毫秒為單位–可能引發(fā)中斷異常InterruptedException,因此要進(jìn)行捕獲和處理–“最小時(shí)間”是因?yàn)檫@個(gè)方法只保證在一段時(shí)間后線程回到就緒態(tài),至于它是否能夠獲得CPU運(yùn)行,則要視線程調(diào)度而定,所以,通常線程實(shí)際被暫停的時(shí)間都比指定的時(shí)間要長第12章

程yield()可以給其他同等優(yōu)先級線程一個(gè)運(yùn)行的機(jī)會(huì)如果在就緒隊(duì)列中有其他同優(yōu)先級的線程,

yield()把調(diào)用者放入就緒隊(duì)列尾,并允許其他線程運(yùn)行;如果沒有這樣的線程,則yield()不做任何工作sleep()調(diào)用允許低優(yōu)先級進(jìn)程運(yùn)行,而yield()方法只給同優(yōu)先級進(jìn)程以運(yùn)行機(jī)會(huì)第12章

程線程的基本控制結(jié)束線程–當(dāng)一個(gè)線程從run()方法的結(jié)尾處返回時(shí),它自動(dòng)消亡并不能再被運(yùn)行,可以將其理解為自然死亡–利用stop()方法強(qiáng)制停止,可以將其理解為強(qiáng)迫死亡,這種方法必須用于Thread類的特定實(shí)例中第12章

程強(qiáng)迫死亡Stoppublic

class

xyz

implements

Runnable{……

//執(zhí)行線程的主要操作}public

class

ThreadTest{public

static

void

main(String

args[]){Runnable

r

=

newxyz();Thread

t

=

newThread(r);t.start();//相應(yīng)的操作if

(time_to_kill){t.stop();}}}第12章

程結(jié)束線程例利用Thread類中的靜態(tài)方法currentThread()來引用正在運(yùn)行的線程,見例11-6–在這個(gè)例子中,執(zhí)行stop()將破壞當(dāng)前的運(yùn)行環(huán)境,因而run()中的循環(huán)在此情況下將不再運(yùn)行例11-6public

class

xyz

implements

Runnable{public

void

run(){while(true){……//執(zhí)行線程的主要操作if

(time_to_die){Thread.currentThread().stop();}}}}第12章

程檢查線程isAlive()–獲取一個(gè)線程是否還在活動(dòng)狀態(tài)的信息。–活動(dòng)狀態(tài)不意味著這個(gè)線程正在執(zhí)行,而只說明這個(gè)線程已被啟動(dòng),并且既沒有運(yùn)行

stop(),也尚未運(yùn)行完方法run()。第12章

程掛起線程暫停一個(gè)線程稱為掛起。在掛起之后,必須重新喚醒線程進(jìn)入運(yùn)行掛起線程的方法sleep()線程不是休眠期滿后就立刻被喚醒,因?yàn)榇藭r(shí)其他線程能正在執(zhí)行,重新調(diào)度只在以下幾種情況下才會(huì)發(fā)生:被喚醒的線程具有更高的優(yōu)先級;正在執(zhí)行的線程因?yàn)槠渌虮蛔枞?;程序處于支持時(shí)間片的系統(tǒng)中suspend()和resume()join()

:引起現(xiàn)行線程等待,直至方法join所調(diào)用的線程結(jié)束第12章

程程序11-4suspend()和resume()程序11-4說明:線程t在運(yùn)行到suspend()以后被強(qiáng)制掛起,暫停運(yùn)行,直到主線程調(diào)用t.resume()時(shí)才被重新喚醒;一個(gè)線程只能被不同于它自身的線程所喚醒,因?yàn)樵趫?zhí)行suspend()方法以后,這個(gè)線程其后的代碼都不會(huì)被執(zhí)行到,而該線程又依賴于其后的resume語句將其喚醒,所以千萬不能將resume()用在已被掛起的線程語句中第12章

程class

xyz

implements

Runnable{public

void

run(){……//執(zhí)行線程的主要操作//暫停線程運(yùn)行,直到被其他線程喚醒Thread.currnetThread().suspend();……

//線程繼續(xù)運(yùn)行...}}class

Usexyz{public

static

void

main(String

args[]){Runnable

r

=

newxyz();Thread

t

=

newThread(r);t.start();//暫停當(dāng)前線程運(yùn)行,以使得已經(jīng)被阻塞得xyz的實(shí)例得以運(yùn)行Thread.sleep(1000);//xyz實(shí)例被suspend()方法暫停,將控制權(quán)返還給主線程,//并由主線程重新喚醒線程tt.resume();Thread.yield();}}第12章

程線程對內(nèi)存、數(shù)據(jù)的共享如push(a):idx++;data[i]=aclass

Stack{int

idx

=

0;char

data[]

=

newchar[6];public

void

push(char

c){data[idx]

=

c;idx

++;}public

charpop(){idx

--;returndata[idx];}}12.2.3

線程同步1、問題的提出線程執(zhí)行的不確定性引起執(zhí)行結(jié)果的不穩(wěn)定如線程A:

A1-A2

線程B:

B1-B2pop():

取出data[i];idx--;第12章

程一個(gè)代表?xiàng)5念惱?1-8錯(cuò)誤情況1–假設(shè)線程a負(fù)責(zé)加入字符,線程b負(fù)責(zé)移出字符。線程a剛剛加入了一個(gè)字符,例如是r,但是尚未遞增索引值,由于某種原因,恰恰這時(shí)它被搶占了:buffer |

p|

q|

r|

|

|

|idx=2

^idx=3第12章

程錯(cuò)誤情況2如果此時(shí)線程b正在等待移出一個(gè)字符,當(dāng)線程a處于等待狀態(tài)時(shí),線程b就得到了運(yùn)行機(jī)會(huì)。這樣,在進(jìn)入方法pop()時(shí),數(shù)據(jù)狀態(tài)已經(jīng)是錯(cuò)誤的。pop方法將繼續(xù)遞減索引值,idx變?yōu)?buffer |

p|

q|

r|

|

|

|idx=1

^操作后將返回字符

“q”,而忽略字符

“r”第12章

程例子說明產(chǎn)生這種問題的原因是對共享資源訪問的不完整性–完整性稱為共享數(shù)據(jù)操作的同步,共享數(shù)據(jù)叫做條件變量解決問題的方法–禁止線程在完成代碼關(guān)鍵部分時(shí)被切換–提供一個(gè)特殊的鎖定標(biāo)志來處理數(shù)據(jù) (Java采用的方法

)第12章

程對象的鎖定標(biāo)志“對象互斥鎖” (又稱為監(jiān)視器、管程)–實(shí)現(xiàn)不同線程對共享數(shù)據(jù)操作的同步。“對象互斥鎖”阻止多個(gè)線程同時(shí)訪問同一個(gè)條件變量。Java可以為每一個(gè)對象的實(shí)例配有一個(gè)“對象互斥鎖”實(shí)現(xiàn)“對象互斥鎖”兩種方法–用關(guān)鍵字volatile來聲明一個(gè)共享數(shù)據(jù)(變量);–用關(guān)鍵字synchronized來聲明一個(gè)操作共享數(shù)據(jù)的方法或一段代碼。第12章

程例11-9class

stack{int

idx

=

0;char

data[]

=

newchar[6];public

void

push(char

c){synchronized

(this){data[idx]

=

c;idx

++;}}…………public

charpop(){synchronized

(this){idx

--;returndata[idx];}}}增加了一個(gè)對synchronized(this)的調(diào)用增加了一個(gè)對

synchronized(this)的調(diào)用第12章

程過程圖示線程1...因等待同步資源而掛起的線程隊(duì)列第12章

程同步方法用synchronized來標(biāo)識(shí)的代碼段或方法即為“對象互斥鎖

”鎖住的部分。如果一個(gè)程序內(nèi)有兩個(gè)或以上的方法使用synchronized標(biāo)志,則它們在同一個(gè)“對象互斥鎖”管理之下一般情況下,都使用synchronized關(guān)鍵字在方法的層次上實(shí)現(xiàn)對共享資源操作的同步,很少使用volatile關(guān)鍵字聲明共享變量第12章

程synchronized()語句寫法標(biāo)準(zhǔn)寫法(public

void

push(char

c){}}簡pu潔blic的sy寫nch法ronized

void

push(char

c){……}把synchronized用做方法的修飾synch更ron為ized妥(th字帖is){,)則整個(gè)方法都將視作同步…………塊,這可能會(huì)使持有鎖定標(biāo)記

的時(shí)間比實(shí)際需要的時(shí)間要長,從而降低效率第12章

程死鎖步以線程運(yùn)行完同繼續(xù)運(yùn)行,所每個(gè)線程都程序塊。而哪個(gè)線程都線程2pen線程1note死鎖情況發(fā)生在一個(gè)線程等待另一個(gè)線程所持有的鎖,而第二個(gè)線程又在等把“待pen第”給一我,個(gè)我線程持有的鎖的時(shí)候。不能繼續(xù)才運(yùn)能給行你,“no除te”非另一恰恰因?yàn)槟膫€(gè)線程都不能無法運(yùn)行完同步程序塊把“note”給我,我才能給你“pen”第12章

程死鎖問題程序死鎖問題見程序11-6第12章

程解決死鎖問題的方法給資源施加排序制定一個(gè)規(guī)則來決定以何種順序來獲得這些鎖,并在整個(gè)程序中遵循這個(gè)順序

–參考操作系統(tǒng)方面的相關(guān)書籍第12章

程線程交互為什么兩個(gè)線程需要交互呢?–涉及到多線程間共享數(shù)據(jù)操作時(shí),除了同步問題之外,還會(huì)遇到如何控制相互交互的線程之間的運(yùn)行進(jìn)度,即多線程的同步問題–生產(chǎn)者——消費(fèi)者問題生產(chǎn)者比消費(fèi)者快時(shí),消費(fèi)者漏掉一些數(shù)據(jù)消費(fèi)者比生產(chǎn)者快時(shí),消費(fèi)者取相同的數(shù)據(jù)生產(chǎn)者消費(fèi)者共享對象putget第12章

程在這個(gè)問題中,兩個(gè)線程要共享貨架這一臨界資源,需要在某些時(shí)刻(貨空/貨滿)協(xié)調(diào)它們的工作,即貨空時(shí)消費(fèi)者應(yīng)等待,而貨滿時(shí)生產(chǎn)者應(yīng)等待。為了不致于發(fā)生混亂,還可進(jìn)一步規(guī)定:當(dāng)生產(chǎn)者往貨架上放貨物時(shí)不允許消費(fèi)者取貨物,當(dāng)消費(fèi)者從貨架上取貨物時(shí)不允許生產(chǎn)者放貨物。這種機(jī)制在操作系統(tǒng)中稱為線程間的同步。在同步機(jī)制中,將那些訪問臨界資源的程序段稱為臨界區(qū)。第12章

程問題的解決同步:用synchronized關(guān)鍵字前綴給針對共享資源的操作加鎖;同步方法、同步塊synchronized

void

push() synchronized

int

pop()實(shí)現(xiàn)機(jī)制:管程線程間需協(xié)調(diào)與通訊:生產(chǎn)者/消費(fèi)者問題wait()與notify()Object類的方法:public

final

voidwait():令當(dāng)前線程掛起并放棄管程,同步資源解鎖,使別的線程可訪問并修改共享資源,而當(dāng)前線程排隊(duì)等候再次對資源的訪問notify()喚醒正在排隊(duì)等待資源管程的線程中優(yōu)先級最高者,使之執(zhí)行并擁有資源的管程wait()+notify()+標(biāo)志變量:可協(xié)調(diào)、同步不同線程的工作第12章

程在Java系統(tǒng)中,臨界區(qū)程序段是用關(guān)鍵字“synchronized”來標(biāo)注,并通過一個(gè)稱為監(jiān)控器的系統(tǒng)軟件來管理的。當(dāng)執(zhí)行被冠以

synchronized的程序段即臨界區(qū)程序時(shí),監(jiān)控器將這段程序(訪問的臨界資源)加鎖,此時(shí),稱該線程占有臨界資源,直到這段程序執(zhí)行完,才釋放鎖。只有鎖被釋放后,其他線程才可以訪問這些臨界資源。用關(guān)鍵字synchronized定義臨界區(qū)的語句形式是:synchronized

(expression)

statement其中,expression代表類的名字,它是可選項(xiàng);statement可以是一個(gè)方法,也可以是一個(gè)語句或一個(gè)語句塊,最常見的是一個(gè)方法。下面通過一個(gè)例子來說明線程的同步問題。第12章

程Wait_Notify

程序CubbyHole.java創(chuàng)建用戶的線程子類Producer:產(chǎn)生數(shù)據(jù)(存數(shù)據(jù));

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(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ǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論