




已閱讀5頁(yè),還剩19頁(yè)未讀, 繼續(xù)免費(fèi)閱讀
版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第8章 多線程編程,支持多線程編程是Java語(yǔ)言的又一大特色。多線程是相對(duì)于進(jìn)程或單線程而言的,它具有并發(fā)性、執(zhí)行效率高的特點(diǎn)。本章將對(duì)Java中的多線程編程作初步介紹。,8.1 多線程編程概述 8.2 線程的創(chuàng)建 8.3 線程的優(yōu)先級(jí) 8.4 線程同步 8.5 線程間通信 8.6 線程的控制,Return,8.1 多線程編程概述,本節(jié)介紹多線程編程的基礎(chǔ)知識(shí),包括多線程的基本概念、Java的線程模型(線程優(yōu)先級(jí)、同步性、消息傳遞)等方面的內(nèi)容。,8.1.1 什么是多線程 8.1.2 Java線程模型,Return,8.1.1 什么是多線程,同其他大多數(shù)編程語(yǔ)言不同,Java內(nèi)置支持多線程編程(multithreaded programming)。多線程程序包含兩條或兩條以上并發(fā)運(yùn)行的部分,把程序中每個(gè)這樣的部分都叫作一個(gè)線程(thread)。每個(gè)線程都有獨(dú)立的執(zhí)行路徑,因此多線程是多任務(wù)處理的一種特殊形式。 讀者可能知道多任務(wù)處理,它實(shí)際上被所有的現(xiàn)代操作系統(tǒng)所支持。然而,多任務(wù)處理有兩種截然不同的類型:基于進(jìn)程的和基于線程的。搞清楚兩者的區(qū)別是很重要的。對(duì)大多數(shù)讀者來(lái)說(shuō),基于進(jìn)程的多任務(wù)處理是更熟悉的形式。進(jìn)程(process)本質(zhì)上是一個(gè)執(zhí)行的程序。因此基于進(jìn)程的多任務(wù)處理的特點(diǎn)是允許你的計(jì)算機(jī)同時(shí)運(yùn)行兩個(gè)或更多的程序。舉例來(lái)說(shuō),基于進(jìn)程的多任務(wù)處理使你在運(yùn)用文本編輯器的時(shí)候可以同時(shí)運(yùn)行Java編譯器。在基于進(jìn)程的多任務(wù)處理中,程序是調(diào)度程序所分派的最小代碼單位。 而在基于線程(thread-based)的多任務(wù)處理環(huán)境中,線程是最小的執(zhí)行單位。這意味著一個(gè)程序可以同時(shí)執(zhí)行兩個(gè)或者多個(gè)任務(wù)的功能。例如,一個(gè)文本編輯器可以在打印的同時(shí)格式化文本。所以,多進(jìn)程程序處理“大圖片”,而多線程程序處理細(xì)節(jié)問(wèn)題。,Return,多線程程序比多進(jìn)程程序需要更少的管理費(fèi)用。進(jìn)程是重量級(jí)的任務(wù),需要分配給它們獨(dú)立的地址空間。進(jìn)程間通信是昂貴和受限的。進(jìn)程間的轉(zhuǎn)換也是很需要花費(fèi)的。另一方面,線程是輕量級(jí)的選手。它們共享相同的地址空間并且共同分享同一個(gè)進(jìn)程。線程間通信是便宜的,線程間的轉(zhuǎn)換也是低成本的。當(dāng)Java程序使用多進(jìn)程任務(wù)處理環(huán)境時(shí),多進(jìn)程程序不受Java的控制,而多線程則受Java控制。 多線程可幫助你編寫出CPU最大利用率的高效程序,使得空閑時(shí)間保持最低。這對(duì)Java運(yùn)行的交互式的網(wǎng)絡(luò)互連環(huán)境是至關(guān)重要的,因?yàn)榭臻e時(shí)間是公共的。例如,網(wǎng)絡(luò)的數(shù)據(jù)傳輸速率遠(yuǎn)低于計(jì)算機(jī)處理能力,而本地文件系統(tǒng)資源的讀寫速度也遠(yuǎn)低于CPU的處理能力。當(dāng)然,用戶輸入也比計(jì)算機(jī)慢很多。在傳統(tǒng)的單線程環(huán)境中,程序必須等待每一個(gè)這樣的任務(wù)完成以后才能執(zhí)行下一步盡管CPU有很多空閑時(shí)間。多線程使你能夠獲得并充分利用這些空閑時(shí)間。,8.1.2 Java線程模型,Java運(yùn)行系統(tǒng)在很多方面依賴于線程,所有的類庫(kù)設(shè)計(jì)都考慮到多線程。實(shí)際上,Java使用線程來(lái)使整個(gè)環(huán)境異步。這有利于通過(guò)防止CPU循環(huán)的浪費(fèi)來(lái)減少無(wú)效部分。 為更好地理解多線程環(huán)境的優(yōu)勢(shì),我們可以將它與它的對(duì)照物相比較。單線程系統(tǒng)的處理途徑是使用一種叫作輪詢的事件循環(huán)方法。在該模型中,單線程控制在一無(wú)限循環(huán)中運(yùn)行,輪詢一個(gè)事件序列來(lái)決定下一步做什么。一旦輪詢裝置返回信號(hào)表明已準(zhǔn)備好讀取網(wǎng)絡(luò)文件,事件循環(huán)調(diào)度控制管理到適當(dāng)?shù)氖录幚沓绦颉V钡绞录幚沓绦蚍祷?,系統(tǒng)中沒(méi)有其他事件發(fā)生。這就浪費(fèi)了CPU時(shí)間。這導(dǎo)致了程序的一部分獨(dú)占了系統(tǒng),阻止了其他事件的執(zhí)行??偟膩?lái)說(shuō),單線程環(huán)境,當(dāng)一個(gè)線程因?yàn)榈却Y源時(shí)阻塞(block,掛起執(zhí)行),整個(gè)程序停止運(yùn)行。,Java多線程的優(yōu)點(diǎn)就在于取消了主循環(huán)/輪詢機(jī)制。一個(gè)線程可以暫停而不影響程序的其他部分。例如,當(dāng)一個(gè)線程從網(wǎng)絡(luò)讀取數(shù)據(jù)或等待用戶輸入時(shí)產(chǎn)生的空閑時(shí)間可以被利用到其他地方。多線程允許活的循環(huán)在每一幀間隙中沉睡一秒而不暫停整個(gè)系統(tǒng)。在Java程序中出現(xiàn)線程阻塞,僅有一個(gè)線程暫停,其他線程繼續(xù)運(yùn)行。 線程存在多種狀態(tài)。線程可以正在運(yùn)行(running),只要獲得了CPU時(shí)間它就可以運(yùn)行;運(yùn)行的線程可以被掛起(suspend),并臨時(shí)中斷它的執(zhí)行;一個(gè)掛起的線程可以被恢復(fù)(resume),允許它從停止的地方繼續(xù)運(yùn)行;一個(gè)線程可以在等待資源時(shí)被阻塞(block);在任何時(shí)候,線程可以被終止(terminate),這將立即中斷運(yùn)行。一旦終止,線程不能被恢復(fù)。線程的各狀態(tài)間關(guān)系見(jiàn)教材P190頁(yè)圖8-1所示。,下面簡(jiǎn)要介紹與Java線程相關(guān)的幾個(gè)概念,Java給每個(gè)線程安排優(yōu)先級(jí)以決定與其他線程比較時(shí)該如何對(duì)待該線程。線程優(yōu)先級(jí)是詳細(xì)說(shuō)明線程間優(yōu)先關(guān)系的整數(shù)。作為絕對(duì)值,優(yōu)先級(jí)是毫無(wú)意義的;當(dāng)只有一個(gè)線程時(shí),優(yōu)先級(jí)高的線程并不比優(yōu)先級(jí)低的線程運(yùn)行的快。相反,線程的優(yōu)先級(jí)是用來(lái)決定何時(shí)從一個(gè)運(yùn)行的線程切換到另一個(gè)。這叫“上下文轉(zhuǎn)換”(context switch)。決定上下文轉(zhuǎn)換發(fā)生的規(guī)則很簡(jiǎn)單: l線程可以自動(dòng)放棄控制。在I/O未決定的情況下,睡眠或阻塞由明確的讓步來(lái)完成。在這種假定下,所有其他的線程被檢測(cè),準(zhǔn)備運(yùn)行的最高優(yōu)先級(jí)線程被授予CPU。 l線程可以被高優(yōu)先級(jí)的線程搶占。在這種情況下,低優(yōu)先級(jí)線程不主動(dòng)放棄,處理器只是被先占無(wú)論它正在干什么處理器被高優(yōu)先級(jí)的線程占據(jù)?;旧?,一旦高優(yōu)先級(jí)線程要運(yùn)行,它就執(zhí)行。這叫做有優(yōu)先級(jí)的多任務(wù)處理。 當(dāng)兩個(gè)相同優(yōu)先級(jí)的線程競(jìng)爭(zhēng)CPU周期時(shí),情形有一點(diǎn)復(fù)雜。對(duì)于Windows這樣的操作系統(tǒng),等優(yōu)先級(jí)的線程是在循環(huán)模式下自動(dòng)劃分時(shí)間的。對(duì)于其他一些非Windows操作系統(tǒng)而,如Solaris 2.x,等優(yōu)先級(jí)線程相對(duì)于它們的對(duì)等體自動(dòng)放棄。如果不這樣,其他的線程就不會(huì)運(yùn)行。,1線程優(yōu)先級(jí),2同步性,由于多線程在程序中引入了一個(gè)異步行為,故在需要的時(shí)候必須有加強(qiáng)同步性的方法。舉例來(lái)說(shuō),如果你希望兩個(gè)線程相互通信并共享一個(gè)復(fù)雜的數(shù)據(jù)結(jié)構(gòu),例如鏈表序列,就需要某些方法來(lái)確保它們沒(méi)有相互沖突。也就是說(shuō),你必須防止一個(gè)線程寫入數(shù)據(jù)而另一個(gè)線程正在讀取鏈表中的數(shù)據(jù)。為此,Java在進(jìn)程間同步性的老模式基礎(chǔ)上實(shí)行了另外的一種方法:管程(monitor)。管程是一種由C.A.R.Hoare首先定義的控制機(jī)制。你可以把管程想象成一個(gè)僅控制一個(gè)線程的小盒子。一旦線程進(jìn)入管程,所有線程必須等待直到該線程退出了管程。用這種方法,管程可以用來(lái)防止共享的資源被多個(gè)線程操縱。 很多多線程系統(tǒng)將管程作為程序必須明確的引用和操作的對(duì)象。但Java提供一個(gè)清晰的解決方案,不提供“Monitor”類;相反,每個(gè)對(duì)象都擁有自己的隱式管程,當(dāng)對(duì)象的同步方法被調(diào)用時(shí)管程自動(dòng)載入。一旦一個(gè)線程包含在一個(gè)同步方法中,沒(méi)有其他線程可以調(diào)用相同對(duì)象的同步方法。這就使你可以編寫非常清晰和簡(jiǎn)潔的多線程代碼,因?yàn)橥街С质钦Z(yǔ)言內(nèi)置的。,3消息傳遞,當(dāng)把程序分成若干線程后,就要定義各線程之間的聯(lián)系。用大多數(shù)其他語(yǔ)言規(guī)劃時(shí)必須依賴于操作系統(tǒng)來(lái)確立線程間通信,這樣當(dāng)然要增加花費(fèi)。然而,Java提供了多線程間談話清潔的、低成本的途徑通過(guò)調(diào)用所有對(duì)象都有的預(yù)先確定的方法。Java的消息傳遞系統(tǒng)允許一個(gè)線程進(jìn)入一個(gè)對(duì)象的一個(gè)同步方法,然后在那里等待,一直等到其他線程明確通知它出來(lái)。,Return,Java的多線程系統(tǒng)建立于Thread類、方法以及共伴接口Runnable基礎(chǔ)上。Thread類封裝了線程的執(zhí)行。既然不能直接引用運(yùn)行著的線程的狀態(tài),就要通過(guò)它的代理處理它。于是Thread 實(shí)例產(chǎn)生了。為創(chuàng)建一個(gè)新的線程,程序中必須擴(kuò)展Thread 或?qū)崿F(xiàn)Runnable接口。Thread類定義了好幾種方法來(lái)幫助管理線程,見(jiàn)教材P192頁(yè)表8-1中所列。,4Thread類和Runnable接口,8.2 線程的創(chuàng)建,本節(jié)介紹在Java中如何創(chuàng)建線程。主要內(nèi)容包括主線程、多線程的創(chuàng)建、相關(guān)方法的使用等。,8.2.1 關(guān)于主線程 8.2.2 創(chuàng)建一個(gè)線程 8.2.3 創(chuàng)建多線程 8.2.4 使用isAlive()和join(),Return,8.2.1 關(guān)于主線程,當(dāng)Java程序啟動(dòng)時(shí),一個(gè)線程立刻運(yùn)行,該線程通常就叫做程序的主線程(main thread),因?yàn)樗浅绦蜷_(kāi)始時(shí)就執(zhí)行的。主線程的重要性主要體現(xiàn)在兩方面: l它是產(chǎn)生其他子線程的線程; l通常它必須最后完成執(zhí)行,因?yàn)樗鼒?zhí)行各種關(guān)閉動(dòng)作。 盡管主線程在程序啟動(dòng)時(shí)自動(dòng)創(chuàng)建,但它可以由一個(gè)Thread對(duì)象控制。為此,必須調(diào)用方法currentThread()獲得它的一個(gè)引用,currentThread()是Thread類的公有的靜態(tài)成員。它的一般形式如下 static Thread currentThread() 該方法返回一個(gè)調(diào)用它的線程的引用。一旦獲得主線程的引用,就可以像控制其他線程那樣控制主線程。 下面我們考察一下教材P192193頁(yè)的程序代碼。 在上面的程序中,當(dāng)前線程(當(dāng)然是主線程)的引用通過(guò)調(diào)用currentThread()獲得,該引用保存在局部變量t中。然后,程序顯示了線程的信息。接著,程序調(diào)用setName()改變線程的內(nèi)部名稱,線程信息又被顯示。然后,一個(gè)循環(huán)數(shù)從5開(kāi)始遞減,每數(shù)一次暫停一秒。暫停是由sleep()方法來(lái)完成的,sleep()語(yǔ)句明確規(guī)定延遲時(shí)間是1毫秒。請(qǐng)讀者注意循環(huán)外的try/ catch塊。Thread類的sleep()方法可能引發(fā)一個(gè)InterruptedException異常,這種情形會(huì)在其他線程想要打攪沉睡線程時(shí)發(fā)生。本例只是打印了它是否被打斷的消息。在實(shí)際的程序中,必須靈活處理此類問(wèn)題。,Return,8.2.2 創(chuàng)建一個(gè)線程,大多數(shù)情況,通過(guò)實(shí)例化一個(gè)Thread對(duì)象來(lái)創(chuàng)建一個(gè)線程。Java定義了兩種方式: l實(shí)現(xiàn)Runnable 接口; l以繼承Thread類的方式。,創(chuàng)建線程最簡(jiǎn)單的方法就是創(chuàng)建一個(gè)實(shí)現(xiàn)Runnable 接口的類,Runnable抽象了一個(gè)執(zhí)行代碼單元。可以通過(guò)實(shí)現(xiàn)Runnable接口的方法創(chuàng)建每一個(gè)對(duì)象的線程。為實(shí)現(xiàn) Runnable 接口,一個(gè)類僅需實(shí)現(xiàn)一個(gè)run()的簡(jiǎn)單方法,該方法聲明如下: public void run() 在run()中,可以定義代碼來(lái)構(gòu)建新的線程。重要的是:run()方法能夠像主線程那樣調(diào)用其他方法,引用其他類,聲明變量。僅有的不同是:run()在程序中確立另一個(gè)并發(fā)的線程執(zhí)行入口。當(dāng)run()返回時(shí),該線程結(jié)束。 在已經(jīng)創(chuàng)建了實(shí)現(xiàn)Runnable接口的類以后,需要在類內(nèi)部實(shí)例化一個(gè)Thread類的對(duì)象。Thread 類定義了好幾種構(gòu)造函數(shù)。我們會(huì)用到的如下: Thread(Runnable threadOb, String threadName) 在該構(gòu)造函數(shù)中,threadOb是一個(gè)實(shí)現(xiàn)Runnable接口類的實(shí)例。這定義了線程執(zhí)行的起點(diǎn),新線程的名稱由threadName定義。 建立新的線程后,它并不運(yùn)行直到調(diào)用其start()方法,該方法在Thread 類中定義。從本質(zhì)上講,start()執(zhí)行的是一個(gè)對(duì)run()的調(diào)用。start()方法聲明如下: void start( ),下面我們分別對(duì)這兩種方法進(jìn)行介紹:,1實(shí)現(xiàn)Runnable接口,2擴(kuò)展Thread,創(chuàng)建線程的另一個(gè)途徑是創(chuàng)建一個(gè)新類來(lái)擴(kuò)展Thread類,然后再創(chuàng)建該類的實(shí)例。當(dāng)一個(gè)類繼承Thread時(shí),它必須重載run()方法,這個(gè)run()方法是新線程的入口。同時(shí),它也必須調(diào)用start()方法去啟動(dòng)新線程執(zhí)行。,Return,到這里,讀者可能會(huì)奇怪為什么Java有兩種創(chuàng)建子線程的方法,哪一種更好呢。所有的問(wèn)題都?xì)w于一點(diǎn)。Thread類定義了多種方法可以被派生類重載。對(duì)于所有的方法,唯一的必須被重載的是run()方法。這當(dāng)然是實(shí)現(xiàn)Runnable接口所需的同樣的方法。很多Java程序員認(rèn)為類僅在它們被加強(qiáng)或修改時(shí)被擴(kuò)展。因此,如果你不重載Thread的其他方法,最好只實(shí)現(xiàn)Runnable 接口,這當(dāng)然由自己決定。在本章的其他部分,我們應(yīng)用實(shí)現(xiàn)Runnable接口的類來(lái)創(chuàng)建線程。,3選擇合適的方法,8.2.3 創(chuàng)建多線程,到目前為止,我們僅用到兩個(gè)線程:主線程和一個(gè)子線程。然而,我們完全可以創(chuàng)建所需的更多線程。例如,教材P197198頁(yè)的程序創(chuàng)建了3個(gè)子線程。詳細(xì)分析該程序。,Return,8.2.4 使用isAlive()和join(),如前所述,我們一般是希望主線程最后結(jié)束。在上面的例子中,這點(diǎn)是通過(guò)在main()中調(diào)用sleep()來(lái)實(shí)現(xiàn)的,經(jīng)過(guò)足夠長(zhǎng)時(shí)間的延遲以確保所有子線程都先于主線程結(jié)束。然而,這并不是一個(gè)好的解決方法。因?yàn)橛袝r(shí)候存在這個(gè)問(wèn)題:一個(gè)線程如何知道另一線程已經(jīng)結(jié)束?幸運(yùn)的是,Thread類提供了解決此問(wèn)題的有效方法。 有兩種方法可以判定一個(gè)線程是否結(jié)束:第一,可以在線程中調(diào)用isAlive()。這種方法由Thread定義,它的一般形式如下 final boolean isAlive() 如果所調(diào)用線程仍在運(yùn)行,isAlive()方法返回true,如果不是則返回false。 但isAlive()很少用到,等待線程結(jié)束的更常用的方法是調(diào)用join(),描述如下 final void join() throws InterruptedException 該方法等待所調(diào)用線程結(jié)束,該名字來(lái)自于要求線程等待直到指定線程參與的概念。join()的附加形式允許給等待指定線程結(jié)束定義一個(gè)最大時(shí)間。詳細(xì)分析教材P199200頁(yè)的程序。,Return,8.3 線程的優(yōu)先級(jí),線程優(yōu)先級(jí)被線程調(diào)度用來(lái)判定何時(shí)某個(gè)線程允許運(yùn)行。理論上,優(yōu)先級(jí)高的線程比優(yōu)先級(jí)低的線程獲得更多的CPU時(shí)間。實(shí)際上,線程獲得的CPU時(shí)間通常由包括優(yōu)先級(jí)在內(nèi)的多個(gè)因素決定。一個(gè)優(yōu)先級(jí)高的線程自然比優(yōu)先級(jí)低的線程優(yōu)先。 理論上,等優(yōu)先級(jí)線程有同等的權(quán)利使用CPU,但你必須小心。需要記住的是,Java是被設(shè)計(jì)成能在很多環(huán)境下工作的。不同環(huán)境下實(shí)現(xiàn)多任務(wù)處理從本質(zhì)上來(lái)看是可能的。為安全起見(jiàn),等優(yōu)先級(jí)線程有時(shí)候也受到控制。這保證了所有線程在無(wú)優(yōu)先級(jí)的操作系統(tǒng)下都有機(jī)會(huì)運(yùn)行。實(shí)際上,在無(wú)優(yōu)先級(jí)的環(huán)境下,多數(shù)線程仍然有機(jī)會(huì)運(yùn)行,因?yàn)楹芏嗑€程不可避免地會(huì)遭遇阻塞,例如等待輸入輸出。遇到這種情形,阻塞的線程掛起,其他線程運(yùn)行。但是如果你希望多線程執(zhí)行得順利的話,最好不要采用這種方法。同樣,有些類型的任務(wù)是占CPU的。對(duì)于這些支配CPU類型的線程,有時(shí)你希望能夠支配它們,以便使其他線程可以運(yùn)行。 設(shè)置線程的優(yōu)先級(jí),用setPriority()方法,該方法也是Thread的成員。它的通常形式為 final void setPriority(int level) 這里,level指定了對(duì)所調(diào)用的線程的新的優(yōu)先權(quán)的設(shè)置。Level的值必須在MIN_PRIORITY到MAX_PRIORITY范圍內(nèi)。通常,它們的值分別是1和10。要返回一個(gè)線程為默認(rèn)的優(yōu)先級(jí),指定NORM_PRIORITY,通常值為5。這些優(yōu)先級(jí)在Thread中都被定義為final型變量。,Return,8.4 線程同步,當(dāng)兩個(gè)或兩個(gè)以上的線程需要共享資源,它們需要某種方法來(lái)確定資源在某一刻僅被一個(gè)線程占用,達(dá)到此目的的過(guò)程叫做同步(synchronization)。Java為此提供了獨(dú)特的、很有效的支持機(jī)制。,8.4.1 使用同步方法 8.4.2 同步語(yǔ)句,Return,8.4.1 使用同步方法,同步的關(guān)鍵是管程(也叫信號(hào)量,即semaphore)的概念。管程是一個(gè)互斥獨(dú)占鎖定的對(duì)象,或稱互斥體(mutex)。在給定的時(shí)間,僅有一個(gè)線程可以獲得管程。當(dāng)一個(gè)線程需要鎖定時(shí),它必須進(jìn)入管程。所有其他的試圖進(jìn)入已經(jīng)鎖定的管程的線程必須掛起直到第一個(gè)線程退出管程。這些其他的線程被稱為等待管程。 我們可以用兩種方法同步化代碼。通過(guò)調(diào)用sleep(),call()方法允許執(zhí)行轉(zhuǎn)換到另一個(gè)線程。兩者都包括synchronized關(guān)鍵字的運(yùn)用。分析教材P204頁(yè)的示例。,Return,8.4.2 同步語(yǔ)句,盡管在創(chuàng)建的類的內(nèi)部創(chuàng)建同步方法是獲得同步的簡(jiǎn)單和有效的方法,但它并非在任何時(shí)候都有效。假設(shè)你想獲得不為多線程訪問(wèn)設(shè)計(jì)的類對(duì)象的同步訪問(wèn),也就是該類沒(méi)有用到synchronized方法。而且,該類不是你自己,而是第三方創(chuàng)建的,就不能獲得它的源代碼。這樣,就不能在相關(guān)方法前加synchronized修飾符。怎樣才能使該類的一個(gè)對(duì)象同步化呢?解決的方法很簡(jiǎn)單:只需將對(duì)這個(gè)類定義的方法的調(diào)用放入一個(gè)synchronized塊內(nèi)就可以了。 下面是synchronized語(yǔ)句的一般形式 synchronized(object) / statements to be synchronized 其中,object是對(duì)同步對(duì)象的引用。如果你想要同步的只是一個(gè)語(yǔ)句,那么不需要花括號(hào)。一個(gè)同步塊確保對(duì)object成員方法的調(diào)用僅在當(dāng)前線程成功進(jìn)入object管程后發(fā)生。,Return,8.5 線程間通信,前面的例子無(wú)條件地阻塞了其他線程異步訪問(wèn)某個(gè)方法。Java對(duì)象中隱式管程的應(yīng)用是很強(qiáng)大的,但是我們可以通過(guò)進(jìn)程間通信達(dá)到更微妙的境界,這在Java中是很簡(jiǎn)單的。,8.5.1 Java中的線程通訊 8.5.2 關(guān)于死鎖,Return,8.5.1 Java中的線程通訊,多線程通過(guò)把任務(wù)分成離散的和合乎邏輯的單元代替了事件循環(huán)程序。線程還有另外一個(gè)優(yōu)點(diǎn):它遠(yuǎn)離了輪詢。輪詢通常由重復(fù)監(jiān)測(cè)條件的循環(huán)實(shí)現(xiàn)。一旦條件成立,就要采取適當(dāng)?shù)男袆?dòng)。這浪費(fèi)了CPU時(shí)間。 為避免輪詢,Java包含了通過(guò)wait(),notify()和notifyAll()方法實(shí)現(xiàn)的一個(gè)進(jìn)程間通信機(jī)制。這些方法在對(duì)象中是用final方法實(shí)現(xiàn)的,所以所有的類都含有它們。這三個(gè)方法僅在synchronized方法中才能被調(diào)用。盡管這些方法從計(jì)算機(jī)科學(xué)遠(yuǎn)景方向上來(lái)說(shuō)具有概念的高度先進(jìn)性,實(shí)際中用起來(lái)卻是很簡(jiǎn)單的。 wait()告知被調(diào)用的線程放棄管程進(jìn)入睡眠直到其他線程進(jìn)入相同管程并且調(diào)用notify()。 notify()恢復(fù)相同對(duì)象中第一個(gè)調(diào)用wait()的線程。 notifyAll()恢復(fù)相同對(duì)象中所有調(diào)用wait()的線程。 這些方法在Object中被聲明,如下所示 final void wait() throws InterruptedException final void notify() final void notifyAll() wait()存在的另外的形式允許你定義等待時(shí)間。分析教材P207210頁(yè)的程序段。,Return,8.5.2 關(guān)于死鎖,需要避免的與多任務(wù)處理有關(guān)的特殊錯(cuò)誤類型是死鎖(deadlock)。死鎖發(fā)生在當(dāng)兩個(gè)線程對(duì)一對(duì)同步對(duì)象有循環(huán)依賴關(guān)系時(shí)。例如,假定一個(gè)線程進(jìn)入了對(duì)象X的管程而另一個(gè)線程進(jìn)入了對(duì)象Y的管程。如果X的線程試圖調(diào)用Y的同步方法,它將像預(yù)料的一樣被鎖定。而Y的線程同樣希望調(diào)用X的一些同步方法,線程永遠(yuǎn)等待,因?yàn)闉榈竭_(dá)X,必須釋放自己的Y的鎖定以使第一個(gè)線程可以完成。死鎖是很難調(diào)試的錯(cuò)誤,這是因?yàn)椋旱谝?,通常它極少發(fā)生,只有到兩線程的時(shí)間段剛好符合時(shí)才能發(fā)生;第二,它可能包含多于兩個(gè)的線程和同步對(duì)象。也就是說(shuō),死鎖在比剛講述的例子有更多復(fù)雜的事件序列的時(shí)候可以發(fā)生。 為充分理解死鎖,觀察它的行為是很有用的。教材P211212頁(yè)的例子生成了兩個(gè)類,A和B,分別有foo()和bar()方法。這兩種方法在調(diào)用其他類的方法前有一個(gè)短暫的停頓。主類,名為Deadlock,創(chuàng)建了A和B的實(shí)例,然后啟動(dòng)第二個(gè)線程去設(shè)置死鎖環(huán)境。foo()和bar()方法使用sleep()強(qiáng)迫死鎖現(xiàn)象發(fā)生。 程序死鎖,需要按CTRL-C來(lái)結(jié)束程序。在PC機(jī)上按CTRL-BREAK(或在Solaris下按CTRL-)可以看到全線程和管程緩沖堆。,Return,8.6 線程的控制,本節(jié)討論有關(guān)線程控制的問(wèn)題,包括線程的掛起、恢復(fù)、終止等方面的問(wèn)題。,8.6.1 掛起、恢復(fù)和終止線程 8.6.2 Java 2中的線程控制 8.6.3 使用instanceof,Return,8.6.1 掛起、恢復(fù)和終止線程,有時(shí),線程的掛起是很有用的。例如,一個(gè)獨(dú)立的線程可以用來(lái)顯示當(dāng)日的時(shí)間。如果用戶不希望用時(shí)鐘,線程被掛起。在任何情形下,掛起線程是很簡(jiǎn)單的,一旦掛起,重新啟動(dòng)線程也是一件簡(jiǎn)單的事。 掛起、終止和恢復(fù)線程機(jī)制在Java 2和早期版本中有所不同。盡管你運(yùn)用Java 2的途徑編寫代碼,仍需了解這些操作在早期Java環(huán)境下是如何完成的。例如,也許你需要更新或維護(hù)老的代碼,就需要了解為什么Java 2會(huì)有這樣的變化。因?yàn)檫@些原因,下面內(nèi)容說(shuō)明了執(zhí)行線程控制的原始方法,接著是Java 2的方法。 先于Java2的版本(Java 1.1或更早版本),程序用Thread 定義的suspend() 和 resume() 來(lái)暫停和再啟動(dòng)線程。它們的形式如下。 final void suspend() final void resume() Thread類同樣定義了stop()來(lái)終止線程,其形式如下: void stop() 一旦線程被終止,它不能被resume() 恢復(fù)繼續(xù)運(yùn)行。,Return,8.6.2 Java 2中的線程控制,在Java 2中不能使用suspend(),resume()和stop() 方法來(lái)控制線程,讀者也許會(huì)想那就沒(méi)有辦法來(lái)停止、恢復(fù)和結(jié)束線程,其實(shí)不然。相反,線程必須被設(shè)計(jì)成使用run()方法定期檢查來(lái)判定線程是否應(yīng)該被掛起,恢復(fù)或終止它自己的執(zhí)行。有代表性的,這由建立一個(gè)指示線程狀態(tài)的標(biāo)志變量來(lái)完成。只要該標(biāo)志設(shè)為“running”,run()方法必須繼續(xù)讓線程執(zhí)行。如果標(biāo)志設(shè)為“suspend”,線程必須暫停。若設(shè)為“stop”,線程必須終止。當(dāng)然,編寫這樣的代碼有很多方
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 商業(yè)綜合體智能技術(shù)應(yīng)用與運(yùn)營(yíng)效率考核試卷
- 水電合同范本2017
- 綠墻保養(yǎng)合同范本
- 按摩店轉(zhuǎn)讓合同范本
- 商超促銷員培訓(xùn)課件
- 承包木耳基地合同范本
- 業(yè)務(wù)代理服務(wù)協(xié)議條款及細(xì)則
- 創(chuàng)新醫(yī)療技術(shù)研發(fā)合同2024
- 私營(yíng)店主用人勞動(dòng)合同
- 男女朋友分手協(xié)議書
- PySide學(xué)習(xí)教程
- 數(shù)據(jù)結(jié)構(gòu)英文教學(xué)課件:chapter1 Introduction
- 人教三年級(jí)數(shù)學(xué)下冊(cè)表格式全冊(cè)
- 事業(yè)單位綜合基礎(chǔ)知識(shí)考試題庫(kù) 綜合基礎(chǔ)知識(shí)考試題庫(kù).doc
- 優(yōu)秀教研組評(píng)比制度及實(shí)施細(xì)則
- 譯林初中英語(yǔ)教材目錄
- 物業(yè)交付后工程維修工作機(jī)制
- 農(nóng)作物病蟲害專業(yè)化統(tǒng)防統(tǒng)治管理辦法
- JJF 1752-2019全自動(dòng)封閉型發(fā)光免疫分析儀校準(zhǔn)規(guī)范(高清版)
- GB 1886.300-2018 食品安全國(guó)家標(biāo)準(zhǔn) 食品添加劑 離子交換樹(shù)脂(高清版)
- 食品經(jīng)營(yíng)單位經(jīng)營(yíng)場(chǎng)所和設(shè)備布局、操作流程示意圖模板
評(píng)論
0/150
提交評(píng)論