Java程序設(shè)計(jì)案例教程-ch08_第1頁
Java程序設(shè)計(jì)案例教程-ch08_第2頁
Java程序設(shè)計(jì)案例教程-ch08_第3頁
Java程序設(shè)計(jì)案例教程-ch08_第4頁
Java程序設(shè)計(jì)案例教程-ch08_第5頁
已閱讀5頁,還剩31頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

第8章多線程編程第1頁本章概述本章的學(xué)習(xí)目標(biāo)主要內(nèi)容本章概述前面我們所開發(fā)的程序大多是單線程的,即一個(gè)程序只有一條從頭到尾的執(zhí)行路線。然而,現(xiàn)實(shí)世界中的很多事務(wù)都是有多種途徑同時(shí)運(yùn)作的,例如:效勞器可能需要同時(shí)處理多個(gè)客戶機(jī)的請(qǐng)求,這就需要有多個(gè)線程同時(shí)在工作。多線程編程使得系統(tǒng)資源并不是由某個(gè)執(zhí)行體獨(dú)占,而是由多個(gè)執(zhí)行單元共同擁有,輪換使用。正確使用多線程可以消除系統(tǒng)的瓶頸問題,提高整個(gè)應(yīng)用系統(tǒng)的性能。本章將詳細(xì)介紹Java語言的多線程技術(shù)。通過本章的學(xué)習(xí),讀者應(yīng)該理解線程和進(jìn)程的區(qū)別,掌握J(rèn)ava的多線程編程技術(shù),了解線程的同步和線程間通信等內(nèi)容。第2頁本章的學(xué)習(xí)目標(biāo)了解進(jìn)程和線程的根本概念和區(qū)別掌握創(chuàng)立線程的兩種方法掌握線程同步的概念和方法了解線程的優(yōu)先級(jí)掌握線程間通信的方法第3頁第4頁主要內(nèi)容8.1Java線程模型 8.2創(chuàng)立線程 8.3同步與線程間通信 8.4獲取線程狀態(tài) 8.5本章小結(jié) 8.6思考和練習(xí) 8.1Java線程模型Java對(duì)多線程編程(multithreadedprogramming)提供了內(nèi)置支持。多線程程序包含同時(shí)運(yùn)行的兩個(gè)或多個(gè)局部。這種程序的每一局部被稱為一個(gè)線程,并且每個(gè)線程定義了單獨(dú)的執(zhí)行路徑。因此,多線程是特殊形式的多任務(wù)處理。第5頁進(jìn)程進(jìn)程本質(zhì)上是正在執(zhí)行的程序。在UNIX操作系統(tǒng)中,每個(gè)應(yīng)用程序的執(zhí)行都在操作系統(tǒng)內(nèi)核中登記一個(gè)進(jìn)程標(biāo)志,操作系統(tǒng)根據(jù)分配的標(biāo)志對(duì)應(yīng)用程序的執(zhí)行進(jìn)行調(diào)度和系統(tǒng)資源分配。每個(gè)進(jìn)程都有自己的內(nèi)存單元,進(jìn)程之間是互相獨(dú)立的,一個(gè)進(jìn)程一般不允許訪問其他進(jìn)程的內(nèi)存空間,因此,進(jìn)程間通信非常困難。基于進(jìn)程的多任務(wù)處理就是允許計(jì)算機(jī)同時(shí)運(yùn)行兩個(gè)或更多個(gè)程序的特性。例如,基于進(jìn)程的多任務(wù)處理可以在運(yùn)行Java編譯器的同時(shí)使用文本編輯器或?yàn)g覽網(wǎng)站。在基于進(jìn)程的多任務(wù)處理中,程序是調(diào)度程序能夠調(diào)度的最小代碼單元。第6頁線程線程是比進(jìn)程更小的執(zhí)行單位。如果將進(jìn)程概念一分為二,那么進(jìn)程中的系統(tǒng)資源,可以看成是一個(gè)靜態(tài)的對(duì)象;而程序代碼的執(zhí)行位置,可以看成一個(gè)動(dòng)態(tài)對(duì)象,這個(gè)動(dòng)態(tài)的局部就是線程。進(jìn)程在執(zhí)行過程中擁有獨(dú)立的內(nèi)存單元,而多個(gè)線程共享內(nèi)存,線程之間的通信比較容易解決,從而極大地提高了程序的運(yùn)行效率。在基于線程的多任務(wù)環(huán)境中,最小的可調(diào)度代碼單元是線程,這意味著單個(gè)程序可以同時(shí)執(zhí)行兩個(gè)或更多個(gè)任務(wù)。例如,文本編輯器可以在打印的同時(shí)格式化文本,只要這兩個(gè)動(dòng)作是通過兩個(gè)獨(dú)立的線程執(zhí)行即可。第7頁進(jìn)程和線程的區(qū)別進(jìn)程和線程的區(qū)別可以總結(jié)為如下幾點(diǎn): 一個(gè)程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線程,線程是進(jìn)程的一個(gè)實(shí)體,是CPU調(diào)度和分派的根本單位,它是比進(jìn)程更小的能獨(dú)立運(yùn)行的根本單位。 線程在執(zhí)行過程中與進(jìn)程也是有區(qū)別的,每個(gè)獨(dú)立的線程有一個(gè)程序運(yùn)行的入口、順序執(zhí)行序列和程序的出口;但是線程不能夠獨(dú)立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個(gè)線程的執(zhí)行控制。 一個(gè)線程可以創(chuàng)立和撤銷另一個(gè)線程,同一進(jìn)程中的多個(gè)線程之間可以并發(fā)執(zhí)行。第8頁Java中的線程Java運(yùn)行時(shí)系統(tǒng)在許多方面依賴于線程,并且所有類庫在設(shè)計(jì)時(shí)都考慮了多線程。事實(shí)上,Java通過利用線程使得整個(gè)環(huán)境能夠異步執(zhí)行。在過去幾年,多核系統(tǒng)已經(jīng)變得很普遍了。當(dāng)然,單核系統(tǒng)仍然在廣泛使用。Java的多線程系統(tǒng)在這兩種類型的系統(tǒng)中都可以工作。在單核系統(tǒng)中,并發(fā)執(zhí)行的線程共享CPU,每個(gè)線程得到一片CPU時(shí)鐘周期。所以,在單核系統(tǒng)中,兩個(gè)或更多個(gè)線程不是真正同時(shí)運(yùn)行的,但是空閑時(shí)間被利用了。然而,在多核系統(tǒng)中,兩個(gè)或多個(gè)線程可能是真正同步執(zhí)行的。第9頁線程的狀態(tài)線程有多種狀態(tài):線程可以處于運(yùn)行(running)狀態(tài),只要獲得CPU時(shí)間就準(zhǔn)備運(yùn)行。運(yùn)行的線程可以被掛起(suspended),這會(huì)臨時(shí)停止線程的活動(dòng)。掛起的線程可以被恢復(fù)(resumed),從而允許線程從停止處恢復(fù)執(zhí)行。當(dāng)?shù)却Y源時(shí),線程會(huì)被阻塞(blocked)。在任何時(shí)候,都可以終止線程,這會(huì)立即停止線程的執(zhí)行。線程一旦終止,就不能再恢復(fù)。第10頁線程優(yōu)先級(jí)Java為每個(gè)線程都指定了優(yōu)先級(jí),優(yōu)先級(jí)決定了相對(duì)于其他線程應(yīng)當(dāng)如何處理某個(gè)線程。線程優(yōu)先級(jí)是一些整數(shù),它們指定了一個(gè)線程相對(duì)于另一個(gè)線程的優(yōu)先程度。Thread類中有3個(gè)和線程優(yōu)先級(jí)有關(guān)的成員變量

MAX_PRIORITY、MIN_PRIORITY、NORMAL_PRIORITY。線程優(yōu)先級(jí)的取值范圍為1(Thread.MIN_PRIORITY)~10(Thread.MAX_PRIORITY)。默認(rèn)情況下,線程的優(yōu)先級(jí)為

NORM_PRIORITY(5)。第11頁消息傳遞將程序分隔到獨(dú)立的線程之后,需要定義它們之間相互通信的方式。Java為兩個(gè)或更多個(gè)線程之間的相互通信提供了一種簡(jiǎn)潔的低本錢方式。Java的消息傳遞系統(tǒng)允許某個(gè)線程進(jìn)入對(duì)象的同步方法,然后進(jìn)行等待,直到其他線程顯式地通知這個(gè)線程退出為止。第12頁Thread類和Runnable接口在Java中創(chuàng)立多線程的方法有兩種:繼承Thread類和實(shí)現(xiàn)Runnable接口。Thread類實(shí)現(xiàn)了Runnable接口,并封裝了線程的執(zhí)行。所以,使用實(shí)現(xiàn)接口Runnable的方法與創(chuàng)立Thread類的子類的方法沒有本質(zhì)差異,但由于Java不支持多繼承,任何類只能繼承一個(gè)類,所以當(dāng)已經(jīng)繼承了某一類時(shí),就無法在繼承Thread類,這種情況下只能通過實(shí)現(xiàn)Runnable接口的方法來實(shí)現(xiàn)程序的多線程。第13頁主線程當(dāng)Java程序啟動(dòng)時(shí),會(huì)立即開始運(yùn)行一個(gè)線程,因?yàn)樗浅绦蜷_始時(shí)執(zhí)行的線程,所以這個(gè)線程通常稱為程序的主線程。主線程很重要,因?yàn)槠渌泳€程都是從主線程產(chǎn)生的;而且,主線程必須是最后才結(jié)束執(zhí)行的線程。盡管主線程是在程序啟動(dòng)時(shí)自動(dòng)創(chuàng)立的,但是可以通過Thread對(duì)象對(duì)其進(jìn)行控制。為此,必須調(diào)用Thread的靜態(tài)方法currentThread()來獲取對(duì)主線程的一個(gè)引用。一旦得到對(duì)主線程的引用,就可以像控制其他線程那樣控制主線程了。第14頁第15頁主要內(nèi)容8.1Java線程模型 8.2創(chuàng)立線程 8.3同步與線程間通信 8.4獲取線程狀態(tài) 8.5本章小結(jié) 8.6思考和練習(xí) 8.2創(chuàng)立線程Java提供了兩種創(chuàng)立線程的方法:實(shí)現(xiàn)Runnable接口和擴(kuò)展Thread類。Thread類定義了派生類可以重寫的幾個(gè)方法。在這些方法中,只有一個(gè)方法必須重寫,即run()方法。這也是實(shí)現(xiàn)Runnable接口時(shí)需要實(shí)現(xiàn)的方法。因?yàn)門hread類本身實(shí)現(xiàn)了Runnable接口,所以這兩種創(chuàng)立線程的方法在本質(zhì)上是一樣的。通常,如果不重寫Thread類的其他方法,創(chuàng)立子線程最簡(jiǎn)單的方式就是地實(shí)現(xiàn)Runnable接口。此外,因?yàn)镴ava只有單繼承,當(dāng)一個(gè)類已經(jīng)繼承了其他類時(shí),那么不能通過繼承Thread類來創(chuàng)立線程,此時(shí)只能通過實(shí)現(xiàn)Runnable接口的方式。第16頁實(shí)現(xiàn)Runnable接口創(chuàng)立線程最簡(jiǎn)單的方式就是創(chuàng)立實(shí)現(xiàn)Runnable接口。Runnable接口抽象了一個(gè)可執(zhí)行代碼單元,該接口只有一個(gè)run()方法,它是線程的入口點(diǎn)。可以依托任何實(shí)現(xiàn)了Runnable接口的對(duì)象來創(chuàng)立線程。在run()方法內(nèi)部,定義線程需要執(zhí)行的代碼。run()方法可以調(diào)用其他方法,使用其他類,也可以聲明變量,就像main線程那樣。唯一的區(qū)別是:run()方法是線程的執(zhí)行入口點(diǎn)。當(dāng)run()方法執(zhí)行完了,這個(gè)線程也就結(jié)束了。第17頁創(chuàng)立線程在創(chuàng)立實(shí)現(xiàn)了Runnable接口的類之后,可以使用Thread類的如下構(gòu)造方法來實(shí)例化一個(gè)Thread對(duì)象,這個(gè)Thread對(duì)象就是一個(gè)新的線程。Thread(RunnablethreadOb,StringthreadName)threadOb是實(shí)現(xiàn)了Runnable接口的類的實(shí)例,threadName為新線程的名稱。在創(chuàng)立了新線程之后,只有調(diào)用線程的start()方法,線程才會(huì)運(yùn)行,該方法是在Thread類中聲明的。本質(zhì)上,start()方法執(zhí)行對(duì)run()方法的調(diào)用。第18頁擴(kuò)展Thread類創(chuàng)立線程的第二種方式是創(chuàng)立一個(gè)擴(kuò)展了Thread的新類,然后創(chuàng)立該類的實(shí)例。擴(kuò)展類必須重寫run()方法,run()方法是新線程的入口點(diǎn)。擴(kuò)展類還必須調(diào)用start()方法來開始新線程的執(zhí)行。第19頁線程優(yōu)先級(jí)線程的優(yōu)先級(jí)只有在多個(gè)線程等待CPU調(diào)度時(shí)才有用,理論上,優(yōu)先級(jí)更高的線程比優(yōu)先級(jí)更低的線程會(huì)獲得更多的CPU時(shí)間。實(shí)際上,線程得到的CPU時(shí)間除了依賴于優(yōu)先級(jí)外,通常還依賴于其他幾個(gè)因素(例如,操作系統(tǒng)實(shí)現(xiàn)多任務(wù)的方式可能會(huì)影響CPU時(shí)間的相對(duì)可用性)。具有更高優(yōu)先級(jí)的線程還可能取代更低優(yōu)先級(jí)的線程。例如,當(dāng)一個(gè)低優(yōu)先級(jí)的線程正在運(yùn)行時(shí),需要恢復(fù)一個(gè)更高優(yōu)先級(jí)的線程(例如,從休眠或等待I/O中恢復(fù))時(shí),高優(yōu)先級(jí)的線程將取代低優(yōu)先級(jí)的線程。本例線程數(shù)較少,而且線程運(yùn)行時(shí)間都較短,可能每次運(yùn)行得到的結(jié)果會(huì)不盡相同,高優(yōu)先級(jí)的“線程3”可能并不一定每次都是最先結(jié)束第20頁第21頁主要內(nèi)容8.1Java線程模型 8.2創(chuàng)立線程 8.3同步與線程間通信 8.4獲取線程狀態(tài) 8.5本章小結(jié) 8.6思考和練習(xí) 8.3同步與線程間通信在處理多線程問題時(shí),必須考慮這樣一個(gè)問題:當(dāng)兩個(gè)或多個(gè)線程同時(shí)訪問同一個(gè)數(shù)據(jù),并且一個(gè)線程需要修改這個(gè)數(shù)據(jù)。例如:在鐵路售票系統(tǒng)中,全國(guó)有很多個(gè)網(wǎng)點(diǎn)同時(shí)售票,當(dāng)只剩1張票時(shí),可能有很多售票點(diǎn)都要出售這張票,如何保證這張票只被售一次呢?只剩1張票的時(shí)候,還有多個(gè)線程在售票,所以盡管在程序中有關(guān)于tickets>0的判斷,但還是出現(xiàn)了余票為負(fù)數(shù)的情況。為解決此問題我們需要了解有關(guān)線程同步的知識(shí)。在Java中,同步是通過synchronized關(guān)鍵字來定義的。實(shí)現(xiàn)同步的方法有同步代碼塊和同步方法兩種。第22頁同步當(dāng)兩個(gè)或多個(gè)線程需要訪問共享的資源時(shí),它們需要以某種方式確保每次只有一個(gè)線程使用資源。實(shí)現(xiàn)這一目的的過程稱為同步。通常,完成一項(xiàng)功能是通過方法來完成的,因此,在多線程應(yīng)用中通常將線程中的執(zhí)行方法設(shè)置為同步。在Java中,如果某方法使用synchronized關(guān)鍵字修飾,那么系統(tǒng)將該方法設(shè)置一個(gè)內(nèi)部標(biāo)記。這個(gè)標(biāo)記可以看成是方法執(zhí)行的信號(hào)燈。如果系統(tǒng)需要調(diào)用該方法時(shí),首先檢查該信號(hào)燈的狀態(tài),如果確認(rèn)該方法不是正在被調(diào)用,那么首先將信號(hào)燈置位,然后調(diào)用該方法。當(dāng)方法執(zhí)行結(jié)束后,將該信號(hào)燈復(fù)位,以提供給其他線程繼續(xù)調(diào)用該方法。第23頁同步方法在多線程情況下,如果有一個(gè)或一組方法用來操作對(duì)象的內(nèi)部狀態(tài),那么每次都應(yīng)當(dāng)使用synchronized關(guān)鍵字,以保證狀態(tài)不會(huì)進(jìn)入競(jìng)態(tài)條件。請(qǐng)記住,一旦線程進(jìn)入一個(gè)實(shí)例的同步方法,所有其他線程就都不能再進(jìn)入相同實(shí)例的任何同步方法。但是,仍然可以繼續(xù)調(diào)用同一實(shí)例的非同步局部。第24頁同步方法synchronized還可以用來修飾static方法,static方法屬于類方法,它屬于這個(gè)類,那么static獲取到的鎖,是屬于類的鎖。而非static方法獲取到的鎖,是屬于當(dāng)前對(duì)象的鎖。所以,他們之間不會(huì)產(chǎn)生互斥。第25頁synchronized代碼塊雖然在類中創(chuàng)立同步方法是一種比較容易并且行之有效的實(shí)現(xiàn)同步的方式,但并不是在所有情況下都可以使用這種方式。例如,某個(gè)類沒有針對(duì)多線程訪問進(jìn)行設(shè)計(jì),即類沒有使用同步方法,而又希望同步對(duì)類的訪問。而該類又是由第三方創(chuàng)立的,我們沒有它的源代碼。因此,不能為類中的方法添加synchronized修飾符。對(duì)于這種情況,Java提供了同步代碼塊的方式來同步訪問這種類的對(duì)象??梢院?jiǎn)單地將對(duì)這種類定義的方法的調(diào)用放到synchronized代碼塊中。第26頁synchronized代碼塊synchronized(objRef){ //被同步的語句}其中,objRef是對(duì)被同步對(duì)象的引用。synchronized代碼塊確保對(duì)objRef對(duì)象的成員方法的調(diào)用,只會(huì)在當(dāng)前線程成功進(jìn)入objRef的監(jiān)視器之后發(fā)生。第27頁線程簡(jiǎn)通信Java是通過Object類的wait、notify、notifyAll這幾個(gè)方法來實(shí)現(xiàn)線程間通信的,由于所有的類都是從Object繼承的,因此在任何類中都可以直接使用這些方法。下面是這三個(gè)方法的簡(jiǎn)要說明。

wait:告訴當(dāng)前線程放棄監(jiān)視器并進(jìn)入睡眠狀態(tài),直到其他線程進(jìn)入同一監(jiān)視器并調(diào)用notify方法為止。

notify:?jiǎn)拘淹粚?duì)象監(jiān)視器中調(diào)用wait的第一個(gè)線程。用于類似飯館有一個(gè)空位后通知所有等候就餐的顧客中的第一位可以入座的情況

notifyAll:?jiǎn)拘淹粚?duì)象監(jiān)視器中調(diào)用wait的所有線程,具有最高優(yōu)先級(jí)的線程首先被喚醒并執(zhí)行。用于類似某個(gè)不定期的培訓(xùn)班終于招生滿額后,通知所有學(xué)員都來上課的情況。第28頁線程簡(jiǎn)通信這3個(gè)方法只能在synchronized方法中調(diào)用,即無論線程調(diào)用一個(gè)對(duì)象的wait還是notify方法,該線程必須先得到該對(duì)象的鎖,這樣,notify只能喚醒同一對(duì)象監(jiān)視器中調(diào)用wait的線程,使用多個(gè)對(duì)象監(jiān)視器,我們就可以分組有多個(gè)wait、notify的情況,同組里的wait只能被同組的notify喚醒。如果一個(gè)線程使用的同步方法中用到某個(gè)變量,而此變量又需要其他線程修改后才能符合本線程的需要,那么可以在同步方法中使用wait()方法,使本線程進(jìn)入等待狀態(tài),并運(yùn)行其他線程使用這個(gè)同步方法。其他線程在使用完這個(gè)同步方法的同時(shí),用notifyAll()或notify()方法通知所有的由于使用這個(gè)同步方法而處于等待狀態(tài)的線程,讓它們結(jié)束等待,再次使用這個(gè)同步方法。第29頁第30頁主要內(nèi)容8.1Java線程模型 8.2創(chuàng)立線程 8.3同步與線程間通信 8.4獲取線程狀態(tài) 8.5本章小結(jié) 8.6思考和練習(xí) 8.4獲取線程狀態(tài)線程可以處于許多不同的狀態(tài)。線程的大局部生命周期都在操作系統(tǒng)和JVM的控制中;然而,其中的一些狀態(tài)轉(zhuǎn)換也可以由程序員控制,如前面使用wait()方法可以掛起線程??梢哉{(diào)用Thread類的getState()方法來獲取線程的當(dāng)前狀態(tài),該方法返回Thread.State枚舉值,State是Thread類中定義的枚舉類型,共有6個(gè)枚舉值第31頁狀態(tài)聯(lián)系圖各種狀態(tài)之間的聯(lián)系圖。當(dāng)一個(gè)線程被創(chuàng)立后,其狀態(tài)為NEW,調(diào)用start()方法可以啟動(dòng)線程,此時(shí)線程進(jìn)入RUNNABLE狀態(tài),等待分配CPU或已經(jīng)獲得CPU正在運(yùn)行;當(dāng)執(zhí)行wait()方法時(shí),線程會(huì)進(jìn)入WAITING狀態(tài),等待notify()或notifyAll()方法喚醒線程;當(dāng)線程等待某個(gè)被鎖的共享資源時(shí),線程進(jìn)入BLOCKED狀態(tài),直到獲得鎖再次回到RUNNABLE狀態(tài)等待調(diào)度;如果run()方法執(zhí)行完畢,線

溫馨提示

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