版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、第八章第八章 線程線程 本章導(dǎo)讀本章導(dǎo)讀n Java中的線程n 線程的生命周期n 線程的優(yōu)先級與調(diào)度管理n Thread的子類創(chuàng)建線程n Runable接口n 線程同步n wait()、notify 和notifyAll()方法n 掛起、恢復(fù)和終止線程n 線程的聯(lián)合n 守護(hù)線程1線程概述線程概述 Java語言的一大特點(diǎn)就是內(nèi)置對多線程的支持(java.lang包中的Thread類)。多線程是指同時(shí)存在幾個執(zhí)行體,按幾條不同的執(zhí)行線索共同工作的情況,它使得編程人員可以很方便地開發(fā)出具有多線程功能、能同時(shí)處理多個任務(wù)的功能強(qiáng)大的應(yīng)用程序。雖然執(zhí)行線程給人一種幾個事件同時(shí)發(fā)生的感覺,但這只是一種錯覺
2、,因?yàn)槲覀兊挠?jì)算機(jī)在任何給定的時(shí)刻只能執(zhí)行這些線程中的一個。為了建立這些線程正在同步執(zhí)行的感覺,Java快速地把控制從一個線程切換到另一個線程。 class Qution public static void main(String args) while(true) System.out.println(123); while(true) System.out.println(abc); 28.1 Java中的線程中的線程 程序是一段靜態(tài)的代碼,它是應(yīng)用軟件執(zhí)行的藍(lán)本。進(jìn)程是程序的一次動態(tài)執(zhí)行過程,它對應(yīng)了從代碼加載、執(zhí)行至執(zhí)行完畢的一個完整過程,這個過程也是進(jìn)程本身從產(chǎn)生、發(fā)展至消亡的過程
3、。線程是比進(jìn)程更小的執(zhí)行單位。一個進(jìn)程在其執(zhí)行過程中,可以產(chǎn)生多個線程,形成多條執(zhí)行線索,每條線索,即每個線程也有它自身的產(chǎn)生、存在和消亡的過程,也是一個動態(tài)的概念。 Java應(yīng)用程序總是從主類的main方法開始執(zhí)行。當(dāng)JVM加載代碼,發(fā)現(xiàn)main方法之后,就會啟動一個線程,這個線程稱為“主線程”,該線程負(fù)責(zé)執(zhí)行main方法。那么,在main方法中再創(chuàng)建的線程,就稱為主線程中的線程。如果main方法中沒有創(chuàng)建其他線程,那么當(dāng)main方法執(zhí)行完最后一個語句,即main方法返回時(shí),JVM就會結(jié)束該Java應(yīng)用程序。如果main方法中又創(chuàng)建了其他線程,那么JVM就要在主線程和其他線程之間輪流切換,保
4、證每個線程都有機(jī)會使用CPU資源,main方法即使執(zhí)行完最后的語句,JVM也不會結(jié)束該程序,JVM一直要等到主線程中的所有線程都結(jié)束之后,才結(jié)束該Java應(yīng)用程序。 38.2 線程的生命周期線程的生命周期 在Java語言中,Thread類及其子類創(chuàng)建的對象稱為線程。新建的線程在它的一個完整的生命周期中通常要經(jīng)歷4種狀態(tài)。1)新建:線程對象被聲明并創(chuàng)建時(shí) 2)運(yùn)行:線程對象調(diào)用start()方法3)中斷:中斷的原因消除時(shí),線程可以從中斷處繼續(xù)運(yùn)行. 有4種原因的中斷: *JVM將CPU資源從當(dāng)前線程切換給其他線程 *線程使用CPU資源期間,執(zhí)行了sleep(int millsecond)方法 *
5、線程使用CPU資源期間,執(zhí)行了wait()方法 *線程使用CPU資源期間,執(zhí)行某個操作進(jìn)入阻塞狀態(tài)4)死亡: 釋放分配給線程對象的內(nèi)存。 線程死亡的原因有二: 即執(zhí)行完run()方法中的全部語句或或線程被提前強(qiáng)制性終止,即強(qiáng)制run()方法結(jié)束。 例8-1 Thread的子類WriteWordThread創(chuàng)建了兩個線程。 注注: :上述程序在不同的計(jì)算機(jī)運(yùn)行或在同一臺計(jì)算機(jī)反復(fù)運(yùn)行的結(jié)果不盡相同,輸出結(jié)果依賴當(dāng)前CPU資源的使用情況。為了使結(jié)果盡量不依賴于當(dāng)前CPU資源的使用情況,我們應(yīng)當(dāng)讓線程主動調(diào)用sleep()方法讓出CPU的使用權(quán)進(jìn)入中斷狀態(tài)。 例8-2 4例子8-1效果圖58.3 線
6、程的優(yōu)先級與調(diào)度管理線程的優(yōu)先級與調(diào)度管理 Java虛擬機(jī)中的線程調(diào)度器負(fù)責(zé)管理線程,調(diào)度器把線程的優(yōu)先級分為10個級別,分別用Thread類中的類常量表示。每個Java線程的優(yōu)先級都在常數(shù)1(Thread.MIN PRIORITY)到常數(shù)10(Thread.MAX_PRIORITY)的范圍內(nèi)。如果沒有明確地設(shè)置線程的優(yōu)先級別,每個線程的優(yōu)先級都為常數(shù)5(包括主線)Thread.NORM_PRIORITYThread.NORM_PRIORITY。 線程的優(yōu)先級可以通過setPriority(int grade)方法調(diào)整,這一方法需要一個int類型參數(shù)。有些操作系統(tǒng)只能識別3個級別:1,5,10
7、。 在采用時(shí)間片的系統(tǒng)中,每個線程都有機(jī)會獲得CPU的使用權(quán),以便使用CPU資源執(zhí)行線程中的操作。當(dāng)線程使用CPU資源的時(shí)間結(jié)束后,即使線程沒有完成自己的全部操作,Java調(diào)度器也會中斷當(dāng)前線程的執(zhí)行,把CPU的使用權(quán)切換給下一個排隊(duì)等待的線程,當(dāng)前線程將等待CPU資源的下一次輪回,然后從中斷處繼續(xù)執(zhí)行。 Java調(diào)度器的任務(wù)是使高優(yōu)先級的線程能始終運(yùn)行,一旦時(shí)間片有空閑,則使具有同等優(yōu)先級的線程以輪流的方式順序使用時(shí)間片。 68.4 Thread 的子類創(chuàng)建線程的子類創(chuàng)建線程 在Java語言中,用Thread類或子類創(chuàng)建線程對象。 用戶可以擴(kuò)展 Thread類,但需要重寫父類的run()方法
8、,其目的是規(guī)定線程的具體操作,否則線程就什么也不做,因?yàn)楦割惖膔un()方法中沒有任何操作語句。 例子8-3中除主線程外還有兩個線程,這兩個線程分別在命令行窗口的左側(cè)和右側(cè)順序地一行一行地輸出字符串。主線程負(fù)責(zé)判斷輸出的行數(shù),當(dāng)其中任何一個線程輸出8行后,就結(jié)束進(jìn)程。 本例題中用到了System類中的類方法exit(int n),主線程使用該方法結(jié)束整個程序。 78.5 Runnable接口接口 使用Thread子類創(chuàng)建線程的優(yōu)點(diǎn)是:我們可以在子類中增加新的成員變量,使線程具有某種屬性,也可以在子類中新增加方法,使線程具有某種功能。但是,Java不支持多繼承,Thread類的子類不能再擴(kuò)展其他
9、的類。81Runnable接口與目標(biāo)對象 創(chuàng)建線程的另一個途徑就是用Thread類直接創(chuàng)建線程對象。使用Thread創(chuàng)建線程對象時(shí),通常使用的構(gòu)造方法是: Thread(Runnable target) 該構(gòu)造方法中的參數(shù)是一個Runnable類型的接口,因此,在創(chuàng)建線程對象時(shí)必須向構(gòu)造方法的參數(shù)傳遞一個實(shí)現(xiàn)Runnable接口類的實(shí)例,該實(shí)例對象稱作所創(chuàng)線程的目標(biāo)對象,當(dāng)線程調(diào)用start()方法后,一旦輪到它來享用CPU資源,目標(biāo)對象就會自動調(diào)用接口中的run()方法(接口回調(diào)),這一過程是自動實(shí)現(xiàn)的,用戶程序只需要讓線程調(diào)用start()方法即可,也就是說,當(dāng)線程被調(diào)度并轉(zhuǎn)入運(yùn)行狀態(tài)時(shí),
10、所執(zhí)行的就是run()方法中所規(guī)定的操作。 例8-4不使用Thread類的子類創(chuàng)建線程,而是使用Thread類創(chuàng)建left和right線程。 線程間可以共享相同的內(nèi)存單元(包括代碼與數(shù)據(jù)),并利用這些共享單元來實(shí)現(xiàn)數(shù)據(jù)交換、實(shí)時(shí)通信和必要的同步操作。 例8-5中,線程zhang和cheng使用同一目標(biāo)對象.兩個線程共享目標(biāo)對象的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),(效果如圖8.2所示)。 例8-6中中共有4個線程threadA、threadB、threadC
11、和threadD,其中threadA和threadB的目標(biāo)對象a1,threadC和threadD的目標(biāo)對象是a2。threadA和threadB共享a1的成員number,而threadC和threadD共享a2的成員number。 9例子8-5效果圖102目標(biāo)對象與線程的關(guān) 目標(biāo)對象和線程的關(guān)系有以下兩種情景。1 1)目標(biāo)對象和線程完全解藕)目標(biāo)對象和線程完全解藕 在上述例8-5中,創(chuàng)建目標(biāo)對象的Bank類并沒有組合zhang和cheng線程對象,也就是說Bank創(chuàng)建的目標(biāo)對象bank不包含對象zhang和cheng線程對象的引用(完全解藕)。在這種情況下,目標(biāo)對象經(jīng)常需要通過獲得線程的名
12、字(因?yàn)闊o法獲得線程對象的引用): String name = Thread.currentThread().getName(); String name = Thread.currentThread().getName();以便確定是哪個線程正在占用CPU資源,即被JVM正在執(zhí)行的線程。2 2)目標(biāo)對象組合線程(弱藕合)目標(biāo)對象組合線程(弱藕合) 目標(biāo)對象可以組合線程,即將線程作為自己的成員(弱藕合),比如讓線程zhang和cheng在bank中。當(dāng)創(chuàng)建目標(biāo)對象類組合線程對象時(shí),目標(biāo)對象可以通過獲得線程對象的引用: Thread.currentThread(); Thread.current
13、Thread(); 來確定是哪個線程正在占用CPU資源,即被JVM正在執(zhí)行的線程,如下面的例8-7中代碼所示。113 關(guān)于run()方法中的局部變量 對于具有相同目標(biāo)對象的線程,當(dāng)其中一個線程享用CPU資源時(shí),目標(biāo)對象自動調(diào)用接口中的run()方法,這時(shí)run()方法中的局部變量被分配內(nèi)存空間。當(dāng)輪到另一個線程享用CPU資源時(shí),目標(biāo)對象會再次調(diào)用接口中的run()方法,那么run()方法中的局部變量會再次分配內(nèi)存空間。也就是說,run()方法已經(jīng)啟動運(yùn)行了兩次,分別運(yùn)行在不同的線程中,即運(yùn)行在不同的時(shí)間片內(nèi)。 我們稱run()方法中的局部變量為線程的局部變量,不同線程的run()方法中的局部變
14、量互不干擾,一個線程改變了自己的run()方法中局部變量的值不會影響其他線程的run()方法中的局部變量。 例8-8演示了兩個線程的run()方法中的局部變量互不干擾(效果如圖8.3所示)。 128.6 線程的常用方法線程的常用方法_1 1 1start()start() 線程調(diào)用該方法將啟動線程,使之從新建狀態(tài)進(jìn)入就緒隊(duì)列排隊(duì),一旦輪到它來享用CPU資源時(shí),就可以脫離創(chuàng)建它的主線程獨(dú)立開始自己的生命周期了。 2 2run()run() Thread類的run()方法與Runnable接口中的run()方法的功能和作用相同,都用來定義線程對象被調(diào)度之后所執(zhí)行的操作,都是系統(tǒng)自動調(diào)用而用戶程序不
15、得引用的方法。 Thread類中,run()方法沒有具體內(nèi)容,所以用戶程序需要創(chuàng)建自己的Thread類的子類,并重寫run()方法來覆蓋原來的run()方法。當(dāng)run()方法執(zhí)行完畢,線程就變成死亡狀態(tài)。在線程沒有結(jié)束run()方法之前,不建議讓線程再調(diào)用start()方法,否則將發(fā)生IllegalThreadStateException異常。3 3sleep(int millsecond)sleep(int millsecond) 有時(shí),優(yōu)先級高的線程需要優(yōu)先級低的線程做一些工作來配合它,或者優(yōu)先級高的線程需要完成一些費(fèi)時(shí)的操作,此時(shí)優(yōu)先級高的線程應(yīng)該讓出處理器,使優(yōu)先級低的線程有機(jī)會執(zhí)行。
16、為達(dá)到這個目的,優(yōu)先級高的線程可以在它的run()方法中調(diào)用sleep()方法來使自己放棄處理器資源,休眠一段時(shí)間。休眠時(shí)間的長短由sleep()方法的參數(shù)決定,millsecond是毫秒為單位的休眠時(shí)間。如果線程在休眠時(shí)被打斷,JVM就拋出InterruptedException異常。因此,必須在try-catch語句塊中調(diào)用sleep()方法。138.6 線程的常用方法線程的常用方法_2 4 4isAlive()isAlive() 在線程的run方法結(jié)束之前,即沒有進(jìn)入死亡狀態(tài)之前,線程調(diào)用isAlive()方法返回true,當(dāng)線程進(jìn)入死亡狀態(tài)后(實(shí)體內(nèi)存被釋放),線程仍可以調(diào)用方法isA
17、live(),這時(shí)返回的值是false。 需要注意的是,一個已經(jīng)運(yùn)行的線程在沒有進(jìn)入死亡狀態(tài)時(shí),不要再給線程分配實(shí)體,由于線程只能引用最后分配的實(shí)體,先前的實(shí)體就會成為“垃圾”,并且不會被垃圾收集機(jī)收集掉。內(nèi)存示意如圖8.4、圖8.5所示。 例8-9中,一個線程每隔1秒鐘在命令行窗口輸出機(jī)器的當(dāng)前時(shí)間,在輸出3秒之后,該線程又被分配了實(shí)體,新實(shí)體又開始運(yùn)行。這時(shí),我們在命令行每秒鐘能看見兩行當(dāng)前時(shí)間,因?yàn)槔鴮?shí)體仍然在工作(效果如圖8.6所示)。 5 5currentThread() currentThread() currentThread()方法是Thread類中的類方法,可以用類名調(diào)用,
18、該方法返回當(dāng)前正在使用CPU資源的線程。 6 6interrupt()interrupt() 當(dāng)一些線程調(diào)用sleep()方法處于休眠狀態(tài)時(shí),一個使用CPU資源的其他線程在執(zhí)行過程中,可以讓休眠的線程分別調(diào)用interrupt()方法“吵醒”自己,即導(dǎo)致休眠的線程發(fā)生InterruptedException異常,從而結(jié)束休眠,重新排隊(duì)等待CPU資源。 例8-10中有3個線程zhangXiao、zhengMing和teacher,其中2個線程zhangXiao和zhengMing準(zhǔn)備休眠10秒鐘后,再輸出“早上好!”。teacher線程在輸出3句“上課”后,吵醒休眠的線程:zhangXiao,z
19、hangXiao被吵醒后再吵醒zhengMing(效果如圖8.7所示)。 14圖8.4、圖8.5、圖8.6、圖8.7158.7 線程同步線程同步 問題問題: 兩個或多個線程會同時(shí)訪問同一個變量,并且一個線程需要修改這個變量。我們應(yīng)對這樣的問題做出處理,否則可能發(fā)生混亂。 解決解決: 把修改數(shù)據(jù)的方法用關(guān)鍵字synchronized來修飾。 一個方法使用關(guān)鍵字synchronized修飾后,如果一個線程A占有CPU資源期間,使得該方法被調(diào)用執(zhí)行,同步方法調(diào)用執(zhí)行完畢之前,其他占有CPU資源的線程一旦調(diào)用這個同步方法就會引起堵塞,堵塞的線程要一直等到堵塞的原因消除(同步方法返回),再排隊(duì)等待CPU
20、資源,以便使用這個同步方法。 所謂線程同步就是若干個線程都需要使用一個synchronized修飾的方法。 例子8-11中有兩個線程:accountant和cashier,它們共同擁有一個賬本。它們都可以使用saveOrTake(int number)對賬本進(jìn)行訪問,會計(jì)使用saveOrTake()方法時(shí),向賬本上寫入存錢記錄;出納使用saveOrTake()方法時(shí),向賬本寫入取錢記錄。因此,當(dāng)會計(jì)正在使用saveOrTake()方法時(shí),出納被禁止使用,反之也是這樣。比如,會計(jì)每次使用saveOrTake()方法時(shí),在賬本上存入90萬元,在存入這筆錢時(shí),分3次存完,每存入30萬元,就喝口茶,那
21、么他喝茶休息時(shí)(注意:存錢這件事還沒結(jié)束,即會計(jì)還沒有使用完saveOrTake()方法),出納仍不能使用saveOrTake()方法。我們要保證其中一人使用saveOrTake()方法時(shí),另一個人將必須等待(效果如圖8.8所示)。 16例子8-11效果 如圖8.8178.8 使用使用wait()、notify()和和notifyall()協(xié)調(diào)同步線程協(xié)調(diào)同步線程 wait()wait()、notify()notify()和和notifyAll()notifyAll()都是Object類中的final方法,被所有的類繼承,且不允許重寫。 wait()wait()方法方法可以中斷線程的執(zhí)行,使本
22、線程等待,暫時(shí)讓出CPU的使用權(quán),并允許其他線程這個同步方法。 其他線程如果在使用這個同步方法時(shí)不需要等待,那么它使用完這個同步方法的同時(shí),應(yīng)當(dāng)用notifyAll()notifyAll()方法方法通知所有的由于使用這個同步方法而處于等待的線程結(jié)束等待。曾中斷的線程就會重新排隊(duì)等待CPU資源,以便從剛才的中斷處繼續(xù)執(zhí)行這個同步方法 注意:如果使用notify()notify(),那么只是通知處于等待中的線程的某一個結(jié)束等待。 例子8-12中,為了避免復(fù)雜數(shù)學(xué)算法,我們模擬3個人排隊(duì)買票,每人買1張票。售票員只有1張五元的錢,電影票五元錢一張。張某拿1張二十元的人民幣排在孫某前面買票,孫某拿1張
23、十元的人民幣排在趙的前面買票,趙某拿1張五元的人民幣排在最后。那么,最終的賣票次序應(yīng)當(dāng)是孫、趙、張(效果如圖8.9所示)。18例子8-12效果 如圖8.9198.9 掛起、恢復(fù)和終止線程 所謂掛起所謂掛起一個線程就是讓線程暫時(shí)讓出CPU的使用權(quán)限,暫時(shí)停止執(zhí)行,但停止執(zhí)行的持續(xù)時(shí)間不確定,因此不能使用sleep()方法暫停線程。 如果線程有目標(biāo)對象,那么當(dāng)前線程在占有CPU期間,只要讓其目標(biāo)對象在某個同步方法中調(diào)用wait()就可以掛起當(dāng)前線程。為了恢復(fù)恢復(fù)這個掛起的線程(從曾中斷處繼續(xù)線程的執(zhí)行),其他線程在占有CPU資源期間,讓掛起的線程的目標(biāo)對象在同步方法中調(diào)用notifyAll()方法即可。 如果線程沒有目標(biāo)對象(用Thread子類創(chuàng)建的線程),那么當(dāng)前線程當(dāng)前線程在占有CPU期間,只要在某個同步方法中調(diào)用wait()就可以掛起自己。為了恢復(fù)恢復(fù)這個掛起的線程,其他線程在占有CPU資源期間,讓掛起的線程在同步方法中調(diào)用notifyAll()方法即可。 終止線程終止線程就是讓線程結(jié)束run方法的執(zhí)行進(jìn)入死亡狀態(tài)。 例子8-13中,線程thread每隔一秒鐘輸出一個整數(shù),輸出3個整數(shù)后,該線程掛起;主線程負(fù)責(zé)恢復(fù)thread線程繼續(xù)執(zhí)行. 例子8-14中,thread是用Th
溫馨提示
- 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)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 新疆2024年新疆兵團(tuán)中心血站招聘2人筆試歷年典型考點(diǎn)(頻考版試卷)附帶答案詳解
- 2025年數(shù)控石油深井測井儀項(xiàng)目規(guī)劃申請報(bào)告模板
- 2025年液體制劑機(jī)械項(xiàng)目申請報(bào)告模板
- 2025年兒童醫(yī)院項(xiàng)目申請報(bào)告模板
- 買賣合同協(xié)議書模板五篇
- 2024年航空材料采購合同關(guān)鍵內(nèi)容
- 實(shí)習(xí)月報(bào)范文模板【五篇】
- 公司員工離職申請書集錦7篇
- 酒店實(shí)習(xí)報(bào)告模板錦集5篇
- 2023年教師個人總結(jié)心得大全(四篇)
- 涂鱗、襯膠防腐專項(xiàng)施工方案
- 人工氣道濕化的護(hù)理培訓(xùn)課件
- GB/T 4269.3-2000農(nóng)林拖拉機(jī)和機(jī)械、草坪和園藝動力機(jī)械操作者操縱機(jī)構(gòu)和其他顯示裝置用符號第3部分:草坪和園藝動力機(jī)械用符號
- GB/T 21655.2-2019紡織品吸濕速干性的評定第2部分:動態(tài)水分傳遞法
- GB/T 11618.1-2008銅管接頭第1部分:釬焊式管件
- GB/T 11348.3-1999旋轉(zhuǎn)機(jī)械轉(zhuǎn)軸徑向振動的測量和評定第3部分:耦合的工業(yè)機(jī)器
- GB 18383-2007絮用纖維制品通用技術(shù)要求
- 搶救車管理質(zhì)控分析
- 華與華內(nèi)部使用說明書(上)
- 【線性代數(shù)自考練習(xí)題】滇西應(yīng)用技術(shù)大學(xué)專升本真題匯總(附答案解析)
- 英語北京版四年級(上冊)單詞匯總
評論
0/150
提交評論