Java第11章-多線程課件_第1頁(yè)
Java第11章-多線程課件_第2頁(yè)
Java第11章-多線程課件_第3頁(yè)
Java第11章-多線程課件_第4頁(yè)
Java第11章-多線程課件_第5頁(yè)
已閱讀5頁(yè),還剩95頁(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)介

1、第11章 多線程學(xué)習(xí)目標(biāo):理解多線程的概念,并能應(yīng)用Thread子類和實(shí)現(xiàn)Runnable接口的對(duì)象來(lái)構(gòu)建Thread對(duì)象;掌握Thread類構(gòu)造方法及線程名稱,熟悉線程優(yōu)先級(jí)及Thread相關(guān)字段,理解線程的生命周期和相關(guān)狀態(tài),并能運(yùn)用線程的其他方法;掌握線程同步與互斥的概念,能夠運(yùn)用同步關(guān)鍵字synchronized來(lái)聲明同步的方法,熟悉生產(chǎn)者與消費(fèi)者模型的定義并能進(jìn)行設(shè)計(jì)。第11章 多線程11. 1 程序、進(jìn)程與線程11. 2 多線程11. 3 線程類Thread11. 4 實(shí)訓(xùn):龜兔賽跑11. 5 習(xí)題11.1程序、進(jìn)程與線程1. 進(jìn)程與線程區(qū)別與聯(lián)系2進(jìn)程和程序區(qū)別及聯(lián)系 程序是存放

2、在磁盤上的一系列代碼和數(shù)據(jù)的執(zhí)行映像,是一個(gè)靜止的實(shí)體;進(jìn)程是具有一定獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位,是一個(gè)執(zhí)行中的程序,它是動(dòng)態(tài)的實(shí)體;線程是進(jìn)程的一個(gè)實(shí)體,是CPU調(diào)度和分派的基本單位,它是比進(jìn)程更小的能獨(dú)立運(yùn)行的基本單位。1. 進(jìn)程與線程區(qū)別與聯(lián)系 1)劃分尺度:線程更小,所以多線程程序并發(fā)性更高。 2)資源分配:進(jìn)程是資源分配的基本單位,同一進(jìn)程內(nèi)多個(gè)線程共享其資源。 3)地址空間:進(jìn)程擁有獨(dú)立的地址空間,同一進(jìn)程內(nèi)多個(gè)線程共享其資源。 4)處理器調(diào)度:線程是處理器調(diào)度的基本單位。 5)執(zhí)行:每個(gè)線程都有一個(gè)程序運(yùn)行的入口,順序執(zhí)

3、行序列和程序的出口,但線程不能單獨(dú)執(zhí)行,必須組成進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)主線程。簡(jiǎn)而言之,一個(gè)程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線程。2進(jìn)程和程序區(qū)別及聯(lián)系(1)進(jìn)程和程序區(qū)別 1)程序只是一組指令的有序集合,它本身沒(méi)有任何運(yùn)行的含義,它只是一個(gè)靜態(tài)的實(shí)體。而進(jìn)程則不同,它是程序在某個(gè)數(shù)據(jù)集上的執(zhí)行。 2)進(jìn)程和程序并不是一一對(duì)應(yīng)的,一個(gè)程序執(zhí)行在不同的數(shù)據(jù)集上就成為不同的進(jìn)程,可以用進(jìn)程控制塊來(lái)唯一地標(biāo)識(shí)每個(gè)進(jìn)程。而這一點(diǎn)正是程序無(wú)法做到的,由于程序沒(méi)有和數(shù)據(jù)產(chǎn)生直接的聯(lián)系,既使是執(zhí)行不同的數(shù)據(jù)的程序,他們指令的集合依然是一樣的,所以無(wú)法唯一地標(biāo)識(shí)出這些運(yùn)行于不同數(shù)據(jù)集上的程序。 3)進(jìn)

4、程還具有并發(fā)性和交往性,這也與程序的封閉性不同。(2)進(jìn)程和程序聯(lián)系 1)進(jìn)程是一個(gè)動(dòng)態(tài)的實(shí)體,它有自己的生命周期。它因創(chuàng)建而產(chǎn)生,因調(diào)度而運(yùn)行,因等待資源或事件而被處于等待狀態(tài),因完成任務(wù)而被撤消,反映了一個(gè)程序在一定的數(shù)據(jù)集上運(yùn)行的全部動(dòng)態(tài)過(guò)程。 2)一個(gè)進(jìn)程肯定有一個(gè)與之對(duì)應(yīng)的程序,而且只有一個(gè)。而一個(gè)程序有可能沒(méi)有與之對(duì)應(yīng)的進(jìn)程(因?yàn)樗鼪](méi)有執(zhí)行),也有可能有多個(gè)進(jìn)程與之對(duì)應(yīng)(運(yùn)行在幾個(gè)不同的數(shù)據(jù)集上)。 3) 進(jìn)程和線程都是由操作系統(tǒng)所體會(huì)的程序運(yùn)行的基本單元,系統(tǒng)利用該基本單元實(shí)現(xiàn)系統(tǒng)對(duì)應(yīng)用的并發(fā)性。 另外,進(jìn)程在執(zhí)行過(guò)程中擁有獨(dú)立的內(nèi)存單元,而多個(gè)線程共享內(nèi)存,從而極大地提高了程序

5、的運(yùn)行效率。線程在執(zhí)行過(guò)程中與進(jìn)程還是有區(qū)別的。每個(gè)獨(dú)立的線程有一個(gè)程序運(yùn)行的入口、順序執(zhí)行序列和程序的出口。但是線程不能夠獨(dú)立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個(gè)線程執(zhí)行控制。如圖11-1所示。圖11-1 進(jìn)程、線程和應(yīng)用程序之間的關(guān)系 簡(jiǎn)而言之,一個(gè)程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線程,線程的劃分尺度小于進(jìn)程,使得多線程程序的并發(fā)性高。11.2多線程11.2.1構(gòu)建Thread子類對(duì)象11.2.2用實(shí)現(xiàn)Runnable接口的對(duì)象構(gòu)建Thread對(duì)象11.2.3 案例1:通過(guò)實(shí)現(xiàn)Runnable接口創(chuàng)建線 先從多任務(wù)談起。多任務(wù)就是能同時(shí)運(yùn)行兩個(gè)或兩個(gè)以上的程序,而且這些程序

6、似乎在同時(shí)運(yùn)行。比如,一邊上網(wǎng)瀏覽網(wǎng)頁(yè)一邊看視頻。這時(shí)計(jì)算機(jī)同時(shí)完成了兩個(gè)任務(wù)。注意,計(jì)算機(jī)只有一個(gè)CPU,實(shí)際上是不能同時(shí)完成兩個(gè)或兩個(gè)以上程序的。計(jì)算機(jī)在完成一項(xiàng)任務(wù)時(shí),CPU還有很多時(shí)間是空閑著的。操作系統(tǒng)正是將CPU的空閑部分分配給其他程序,從而給人的印象是在同時(shí)完成幾項(xiàng)任務(wù)。 多線程通過(guò)把多任務(wù)的原理用到程序的更低一層中,進(jìn)一步發(fā)展了這一原理。在多線程程序中,單個(gè)程序似乎能同時(shí)執(zhí)行多個(gè)任務(wù)。每個(gè)任務(wù)通常被稱為一個(gè)線程(thread),能同時(shí)運(yùn)行一個(gè)以上線程的程序?yàn)槎嗑€程程序。11.2.1構(gòu)建Thread子類對(duì)象 線程模型在Java中,由java.lang.Thread類進(jìn)行定義和描述

7、。程序中的線程都是Thread的實(shí)例,因此用戶可以通過(guò)創(chuàng)建Thread的實(shí)例或定義來(lái)創(chuàng)建Thread子類的實(shí)例和控制自己的線程。繼承Thread類創(chuàng)建一個(gè)新的線程語(yǔ)法如下: 完成線程真正功能的代碼放在類的run()方法中,當(dāng)一個(gè)類繼承Thread類后,就可以在該類中覆蓋run()方法,將實(shí)現(xiàn)該線程的代碼寫入run()方法中,然后同時(shí)調(diào)用Thread類中的start()方法執(zhí)行線程,也就是調(diào)用run()方法。Thread對(duì)象需要一個(gè)任務(wù)來(lái)執(zhí)行,任務(wù)是指線程在啟動(dòng)時(shí)執(zhí)行的工作,該工作的功能代碼被寫在run()方法中。這個(gè)run()方法必須使用如下語(yǔ)法格式: 當(dāng)執(zhí)行一個(gè)線程程序時(shí),就自動(dòng)產(chǎn)生一個(gè)線程

8、,主方法正是在這個(gè)線程上運(yùn)行的。當(dāng)不再啟動(dòng)其他線程時(shí),該程序就為單線程程序,主方法線程啟動(dòng)由Java虛擬機(jī)負(fù)責(zé),程序員負(fù)責(zé)啟動(dòng)自己的線程。語(yǔ)法如下:【例11-1】 從Thread類派生線程舉例。解題思路:在下面的代碼中,類printingThread擴(kuò)展了Thread類。代碼:程序threads.javaimport java.applet.Applet;import java.awt.*;import java.awt.event.*; public class threads extends Applet implements ActionListener Button button1,b

9、utton2; printingThread Thread1; public void init() button1=new Button(Start thread); button1.addActionListener(this); button2=new Button(stop thread); button2.addActionListener(this); add(button2);add(button1); Thread1=new printingThread();public void actionPerformed(ActionEvent e) if(e.getSource()=

10、button1) Thread1.start();if(e.getSource()=button2) Thread1.animateFlag=false;class printingThread extends Thread boolean animateFlag=true; public void run() int i=0;while(animateFlag)System.out.println(hello from Java+i);i+; 運(yùn)行結(jié)果如圖11-2所示。圖11-2 Thread類派生線程運(yùn)行結(jié)果 【說(shuō)明】程序運(yùn)行結(jié)果是在DOS屏幕下不停地打印“Hello from Java

11、i”,i在不斷地增加,直到線程停止。11.2.2用實(shí)現(xiàn)Runnable接口的對(duì)象構(gòu)建Thread對(duì)象 處理多線程的第二種方法是實(shí)現(xiàn)Runnable接口。實(shí)現(xiàn)Runnable接口的語(yǔ)法如下:實(shí)現(xiàn)Runnable接口的類的對(duì)象創(chuàng)建線程以后,該線程的啟動(dòng)將使得對(duì)象的run()方法被調(diào)用。通過(guò)這種方式創(chuàng)建線程的過(guò)程是:把Runnable的一個(gè)實(shí)例作為參數(shù)傳遞給Thread類的一個(gè)構(gòu)造方法,該實(shí)例對(duì)象提供線程體run()。使用Runnable接口啟動(dòng)新的線程的步驟如下:1) 建立Runnable對(duì)象2) 使用參數(shù)為Runnable對(duì)象的構(gòu)造方法創(chuàng)建Thread實(shí)例。3) 調(diào)用start()方法啟動(dòng)線程。

12、 通過(guò)Runnable接口創(chuàng)建線程時(shí)程序員首先需要編寫一個(gè)實(shí)現(xiàn)Runnable接口的類,然后實(shí)例化該類的對(duì)象,這樣就建立了Runnable對(duì)象;接下來(lái)使用相應(yīng)的構(gòu)造方法創(chuàng)建Thread實(shí)例;最后使用該實(shí)例調(diào)用Thread類中的Start()方法啟動(dòng)線程。11.2.3 案例1:通過(guò)實(shí)現(xiàn)Runnable接口創(chuàng)建線解題思路:Welcome類實(shí)現(xiàn)了Runable接口。在Thread1類的main()方法中,以Welcome類的兩個(gè)實(shí)例對(duì)象分別創(chuàng)建了t1,t2兩個(gè)線程,并將線程啟動(dòng)。在創(chuàng)建的線程中,Welcome類的run()方法就是線程體,其中“int i”是線程的數(shù)據(jù)。當(dāng)t1,t2啟動(dòng)時(shí),是從Wel

13、come類對(duì)象的run()開始執(zhí)行,每個(gè)線程分別打印輸出兩個(gè)字符串。代碼:程序Thread.javaimport java.lang.*;public class Thread public static void main(String args) Thread t1=new Thread( new Welcome(); Thread t2=new Thread( new Welcome(); t1.start(); t2.start();class Welcome implements Runnableint i;public void run() while(true)System.ou

14、t.println(歡迎您!+i+);if(i=2) break; 運(yùn)行結(jié)果為:11. 3 線程類Thread11.3.1Thread類構(gòu)造方法及線程名稱11.3.2線程優(yōu)先級(jí)與Thread相關(guān)字段11.3.3線程生命周期與線程狀態(tài)11.3.4線程其他方法11.3.5線程同步與互斥11.3.6同步關(guān)鍵字synchronized11.3.7生產(chǎn)者與消費(fèi)者模型11.3.8案例2:模擬多個(gè)生產(chǎn)者和消費(fèi)者操作公共資源 從上面的介紹中可以了解到,在Java中處理多線程的方法有兩種:使用Thread類和使用Runnable接口。在使用Runnable接口時(shí)需要建立一個(gè)Thread實(shí)例。因此,無(wú)論是通過(guò)Th

15、read類還是Runnable接口建立線程,都必須建立Thread類或它的子類的實(shí)例。11.3.1Thread類構(gòu)造方法及線程名稱1.Thread類構(gòu)造方法2.線程名稱1.Thread類構(gòu)造方法Thread類是java.lang包中的一個(gè)類,從這個(gè)類中實(shí)例化的對(duì)象代表線程,程序中啟動(dòng)一個(gè)新線程需要建立Thread實(shí)例。Thread類的構(gòu)造方法被重載了八次,構(gòu)造方法如下:(1)Runnable target 實(shí)現(xiàn)了Runnable接口的類的實(shí)例。要注意的是Thread類也實(shí)現(xiàn)了Runnable接口,因此,從Thread類繼承的類的實(shí)例也可以作為target傳入這個(gè)構(gòu)造方法。(2)String n

16、ame線程的名子。這個(gè)名子可以在建立Thread實(shí)例后通過(guò)Thread類的setName方法設(shè)置。如果不設(shè)置線程的名子,線程就使用默認(rèn)的線程名:Thread-N,N是線程建立的順序,是一個(gè)不重復(fù)的正整數(shù)。(3)ThreadGroup group當(dāng)前建立的線程所屬的線程組。如果不指定線程組,所有的線程都被加到一個(gè)默認(rèn)的線程組中。關(guān)于線程組的細(xì)節(jié)將在后面的章節(jié)詳細(xì)討論。(4)long stackSize線程棧的大小,這個(gè)值一般是CPU頁(yè)面的整數(shù)倍。如x86的頁(yè)面大小是4KB.在x86平臺(tái)下,默認(rèn)的線程棧大小是12KB。一個(gè)普通的Java類只要從Thread類繼承,就可以成為一個(gè)線程類。并可通過(guò)Th

17、read類的start方法來(lái)執(zhí)行線程代碼。雖然Thread類的子類可以直接實(shí)例化,但在子類中必須要覆蓋Thread類的run方法才能真正運(yùn)行線程的代碼?!纠?1-2】 使用Thread類建立線程舉例。解題思路:下面的代碼建立了兩個(gè)線程:thread1和thread2. Thread1類的run方法,使用this.getName()輸出了當(dāng)前線程的名字,由于在建立線程時(shí)并未指定線程名,因此,所輸出的線程名是系統(tǒng)的默認(rèn)值,也就是Thread-n的形式。在main()程序中,首先輸出主線程的線程名,然后在調(diào)用start方法時(shí),系統(tǒng)自動(dòng)調(diào)用run方法運(yùn)行。代碼:程序Thread1.javapublic

18、 class Thread1 extends Thread public void run() System.out.println(this.getName(); public static void main(String args) System.out.println(Thread.currentThread().getName(); Thread1 thread1 = new Thread1(); Thread1 thread2 = new Thread1 (); thread1.start(); thread2.start(); 運(yùn)行結(jié)果為: 【說(shuō)明】任何一個(gè)Java程序都必須有一

19、個(gè)主線程。一般這個(gè)主線程的名子為main.只有在程序中建立另外的線程,才能算是真正的多線程程序。也就是說(shuō),多線程程序必須擁有一個(gè)以上的線程。2.線程名稱 Thread類有一個(gè)重載構(gòu)造方法可以設(shè)置線程名。除了使用構(gòu)造方法在建立線程時(shí)設(shè)置線程名,還可以使用Thread類的setName方法修改線程名。要想通過(guò)Thread類的構(gòu)造方法來(lái)設(shè)置線程名,必須在Thread的子類中使用Thread類的public Thread(String name)構(gòu)造方法,因此,必須在Thread的子類中也添加一個(gè)用于傳入線程名的構(gòu)造方法。下面的代碼給出了一個(gè)設(shè)置線程名的例子?!纠?1-3】 設(shè)置線程名稱舉例解題思路:

20、在main方法中建立了三個(gè)線程:thread1、thread2和thread3.其中thread1通過(guò)構(gòu)造方法來(lái)設(shè)置線程名,thread2通過(guò)setName方法來(lái)修改線程名,thread3未設(shè)置線程名。代碼:程序Thread2.javapublic class Thread2 extends Thread private String who; public void run() System.out.println(who + : + this.getName(); public Thread2(String who) super(); this.who = who; public Thre

21、ad2(String who, String name) super(name); this.who = who;public static void main(String args)Thread2 thread1 = new Thread2 (thread1, MyThread1);Thread2 thread2 = new Thread2 (thread2); Thread2 thread3 = new Thread2 (thread3);thread2.setName(MyThread2);thread1.start();thread2.start();thread3.start();

22、 運(yùn)行結(jié)果為:【說(shuō)明】在類中有兩個(gè)構(gòu)造方法: (1)public sample2_2(String who) 這個(gè)構(gòu)造方法有一個(gè)參數(shù):who這個(gè)參數(shù)用來(lái)標(biāo)識(shí)當(dāng)前建立的線程。在這個(gè)構(gòu)造方法中仍然調(diào)用Thread的默認(rèn)構(gòu)造方法public Thread()。 (2)public sample2_2(String who, String name) 這個(gè)構(gòu)造方法中的who和第一個(gè)構(gòu)造方法的who的含義一樣,而name參數(shù)就是線程名。在這個(gè)構(gòu)造方法中調(diào)用了Thread類的public Thread(String name)構(gòu)造方法,也就是下面的代碼super(name)。注意:在調(diào)用start方法前后

23、都可以使用setName設(shè)置線程名,但在調(diào)用start方法后使用setName修改線程名,會(huì)產(chǎn)生不確定性,也就是說(shuō)可能在run方法執(zhí)行完后才會(huì)執(zhí)行setName;Thread類的start方法不能多次調(diào)用,如不能調(diào)用兩次thread1.start()方法。否則會(huì)拋出一個(gè)IllegalThreadStateException異常。11.3.2線程優(yōu)先級(jí)與Thread相關(guān)字段 每個(gè)線程都具有各自的優(yōu)先級(jí),線程的優(yōu)先級(jí)可以在程序中表明該線程的重要性,如果有很多線程處于就緒狀態(tài),系統(tǒng)會(huì)根據(jù)優(yōu)先級(jí)來(lái)決定首先使哪個(gè)線程進(jìn)入運(yùn)行狀態(tài)。但這并不意味著低優(yōu)先級(jí)的線程得不到運(yùn)行,而只是它運(yùn)行的幾率比較小,比如垃圾

24、回收線程的優(yōu)先級(jí)就較低。 在默認(rèn)情況下,一個(gè)線程繼承其父線程的優(yōu)先級(jí)。Thread類有3個(gè)有關(guān)線程優(yōu)先級(jí)的靜態(tài)常量:MIN_PRIORITY,MAX_PRIORITY,NORM_PRIORI TY。其中MIN_PRIORITY代表最小優(yōu)先級(jí),通常為1。MAX_PRIORITY代表最高優(yōu)先級(jí),通常為10。NORM _PRIORITY代表普通優(yōu)先級(jí),缺省數(shù)值為5。 對(duì)應(yīng)一個(gè)新建線程,系統(tǒng)會(huì)遵循如下的原則為其指定優(yōu)先級(jí):新建線程將繼承創(chuàng)建它的父線程的優(yōu)先級(jí)。父線程是指執(zhí)行創(chuàng)建新線程對(duì)象語(yǔ)句的線程,它可能是程序的主線程,也可能是某一個(gè)用戶自定義的線程。一般情況下,主線程具有普通優(yōu)先級(jí)。另外,用戶可以通

25、過(guò)調(diào)用Thread類的setPriority(int a)方法來(lái)修改系統(tǒng)自動(dòng)設(shè)定的線程優(yōu)先級(jí),使之符合程序的特定需要。a取值是:Thread. MIN_PRIORITY,Thread. MAX _PRIORITY,Thread. NORM _PRIORITY?!纠?1-4】 設(shè)置線程優(yōu)先級(jí)舉例 解題思路:幾個(gè)新的線程是在main()方法中產(chǎn)生,首先創(chuàng)建線程threadA,它的優(yōu)先級(jí)為8,其次創(chuàng)建形成threadB,它啟動(dòng)之前的優(yōu)先級(jí)為2。為顯示繼承優(yōu)先級(jí)創(chuàng)建threadC和 threadD,它們啟動(dòng)前優(yōu)先級(jí)分別為7,在運(yùn)threadC時(shí),執(zhí)行run方法中的r,創(chuàng)建threadD時(shí),調(diào)用thre

26、adC的r,它繼承threadC的優(yōu)先級(jí),開始線程main,先休眠3秒,然后在已運(yùn)行的threadA中調(diào)用setPriority(),使優(yōu)先級(jí)改成3。代碼:程序SetPriority.javapublic class SetPriority extends Object private static Runnable makeRunnable() Runnable r=new Runnable() public void run() for(int i=0;i5;i+) Thread t=Thread.currentThread(); System.out.println(in run()-優(yōu)

27、先級(jí)=+t.getPriority()+,線程名=+t.getName(); tryThread.sleep(2000); catch(InterruptedException x) return r;public static void main(String args) Thread threadA=new Thread(makeRunnable(),threadA);threadA.setPriority(8);threadA.start();Thread threadB=new Thread(makeRunnable(),threadB);threadB.setPriority(2);

28、threadB.start();Runnable r=new Runnable()public void run() Thread threadC=new Thread(makeRunnable(),threadC); threadC.start(); Thread threadD=new Thread(r,threadD);threadD.setPriority(7);threadD.start();tryThread.sleep(3000); catch(InterruptedException x)threadA.setPriority(3);System.out.println(in

29、main()-threadA.getPriority()=+threadA.getPriority(); 運(yùn)行結(jié)果如下:11.3.3線程生命周期與線程狀態(tài) 線程創(chuàng)建后,啟動(dòng)run就開始了它的生命周期,直至run方法返回時(shí),該線程終止。沒(méi)有任何方法可以強(qiáng)制某個(gè)線程終止運(yùn)行,但可以用interrupt請(qǐng)求終止某個(gè)線程的運(yùn)行。線程也不能連續(xù)不斷地運(yùn)行,否則其他線程就沒(méi)有機(jī)會(huì)執(zhí)行。因此,線程在不同的生命階段有不同的狀態(tài)。線程的狀態(tài)與生命周期如圖11-3所示。圖11-3 線程狀態(tài)轉(zhuǎn)化圖 如上圖所示:當(dāng)用new 創(chuàng)建完一個(gè)線程對(duì)象后,該線程處于新建狀態(tài); 當(dāng)線程對(duì)象調(diào)用了start()后,該線程處于就緒狀

30、態(tài);如果處于就緒狀態(tài)的線程獲得CPU時(shí)間片,開始執(zhí)行run方法的線程執(zhí)行體,該線程處于運(yùn)行狀態(tài);如果線程調(diào)用了sleep()或者調(diào)用了一個(gè)阻塞式I/O方法等,該線程處于阻塞狀態(tài);如果線程的run()執(zhí)行完成或者拋出一個(gè)未捕獲的異常等原因,該線程處于死亡狀態(tài)。新建的線程在它的一個(gè)完整的生命周期中通常要經(jīng)歷如下的5種狀態(tài): 1)新建:當(dāng)一個(gè)Thread類及其子類的對(duì)象被聲明并創(chuàng)建時(shí),新生的線程對(duì)象處于新建狀態(tài)。此時(shí)它已經(jīng)有了相應(yīng)的內(nèi)存空間和 其他資源。2)就緒:處于新建狀態(tài)的線程被啟動(dòng)后,將進(jìn)入線程隊(duì)列排隊(duì)等待CPU服務(wù),此時(shí)它已經(jīng)具備了運(yùn)行的條件,一旦輪到它來(lái)享用CPU資源時(shí),就可以脫離創(chuàng)建它的

31、主線程獨(dú)立開始自己的生命周期了。 3)運(yùn)行:當(dāng)就緒狀態(tài)的線程被調(diào)度并獲得處理器資源時(shí),便進(jìn)入運(yùn)行狀態(tài)。每一個(gè)Thread類及其子類的對(duì)象都有一個(gè)重要的run()方法,當(dāng)線程對(duì)象被調(diào)度執(zhí)行時(shí),它將自動(dòng)調(diào)用本對(duì)象的run()方法,從第一句開始順序執(zhí)行。run()方法定義了這個(gè) 線程的操作和功能。4)阻塞:一個(gè)正在執(zhí)行的線程如果在某些特殊情況下,如被人為掛起或需要執(zhí)行費(fèi)時(shí)的輸入輸出操作時(shí),將讓出CPU并暫時(shí)中止自己的執(zhí)行,進(jìn)入阻塞狀態(tài)。阻塞時(shí)它不能進(jìn)入排隊(duì)隊(duì)列,只有當(dāng)引起阻塞的原因被消除時(shí),線程才可以轉(zhuǎn)入就緒狀態(tài),重新進(jìn)入到線程隊(duì)列中排隊(duì)等待CPU資源,以便從原來(lái)終止處開始繼續(xù)運(yùn)行。5)死亡:處于死

32、亡狀態(tài)的線程不具有繼續(xù)運(yùn)行的能力。線程死亡的原因有二,一個(gè)是正常運(yùn)行的線程完成了它的全部工作,即執(zhí)行完 了run()方法的最后一個(gè)語(yǔ)句并退出,另一個(gè)是線程被提前強(qiáng)制性地終止。11.3.4線程其他方法Thread類提供sleep(),join(),yield(),interrupt(),isAlive(),stop(),currentThread() 等方法,來(lái)控制和協(xié)調(diào)線程。1.sleep()有時(shí)優(yōu)先級(jí)高的線程需要優(yōu)先級(jí)低的線程做一些工作來(lái)配合它,此時(shí)為讓優(yōu)先級(jí)高的線程讓出CPU資源,使得優(yōu)先級(jí)低的線程有機(jī)會(huì)運(yùn)行,可以使用sleep(int millsecond)方法。線程在休眠時(shí)被打斷,JV

33、M就拋出InterruptedException異常。因此,必須在try-catch語(yǔ)句塊中調(diào)用sleep方法。2. join() t.join()方法使當(dāng)前的線程等待,直到t結(jié)束為止,線程恢復(fù)到runnable狀態(tài)。join()有三種格式:1) join()調(diào)用t. join()時(shí),當(dāng)前線程將等待線程t結(jié)束后再繼續(xù)執(zhí)行。2) join(long millis)調(diào)用t. join()時(shí),當(dāng)前線程將等待線程t結(jié)束或最多等待millis(ms)后,再繼續(xù)執(zhí)行。3) join(long millis,int nanos)調(diào)用t. join()時(shí),當(dāng)前線程將等待線程t結(jié)束或最多等待millis(ms)

34、+ nanos(ns)后,再繼續(xù)執(zhí)行。3. yield() 優(yōu)先級(jí)同級(jí)時(shí),調(diào)用該方法后,可以使具有與當(dāng)前線程相同優(yōu)先級(jí)的線程有運(yùn)行的機(jī)會(huì)。如果有多個(gè)相同優(yōu)先級(jí)并且是Runnable狀態(tài),該方法將把調(diào)用yield()方法的線程放入可運(yùn)行線程去排隊(duì),并允許對(duì)首線程運(yùn)行。也就是交出CPU一段時(shí)間,暫停當(dāng)前正在執(zhí)行的線程對(duì)象,并執(zhí)行其他線程。4. interrupt() 當(dāng)線程調(diào)用sleep()方法處于休眠狀態(tài),一個(gè)占有CPU資源的線程可以讓休眠的線程調(diào)用interrupt()方法吵醒自己,即導(dǎo)致線程發(fā)生IllegalThreadStateException異常,從而結(jié)束休眠,重新排隊(duì)等待CPU資源。

35、5.isAlive() 當(dāng)線程調(diào)用start()方法并占有CPU資源后該線程的run()方法開始運(yùn)行,在run()方法沒(méi)有結(jié)束之前調(diào)用isAlive()返回true,當(dāng)線程處于新建態(tài)或死亡態(tài)時(shí)調(diào)用isAlive()返回false。注意:一個(gè)已經(jīng)運(yùn)行的線程在沒(méi)有進(jìn)入死亡態(tài)時(shí),不要再給它分配實(shí)體,由于線程只能引用最后分配的實(shí)體,先前的實(shí)體就成為了垃圾,并且不能被垃圾回收機(jī)制收集。 6.stop() 當(dāng)線程完成運(yùn)行并結(jié)束后,將不能在運(yùn)行。除線程正常運(yùn)行結(jié)束外,還可用其他方法控制其停止,即用stop()方法強(qiáng)行 終止線程。但不提倡采用這種方法,因?yàn)樗菀自斐删€程的不一致??梢耘cinterrupt()聯(lián)

36、合使用。7.currentThread() Thread類的靜態(tài)方法,可以用類名調(diào)用,返回當(dāng)前正在使用CPU資源的線程。8.suspend()與rusume()11.3.5線程同步與互斥 在多線程程序中,線程是共享內(nèi)存,也就是共享資源,因此,有時(shí)會(huì)出現(xiàn)共享數(shù)據(jù)不一致的危險(xiǎn)。如銀行的賬目,同時(shí)有多個(gè)用戶訪問(wèn)同一個(gè)賬號(hào),有借有貸,若程序?qū)€程沒(méi)有管理好,就可能破壞賬目數(shù)據(jù)。Java語(yǔ)言為控制訪問(wèn)共享資源,提供了高層同步的概念。 線程之間通過(guò)對(duì)資源的競(jìng)爭(zhēng),包括共享的數(shù)據(jù)和硬件資源,所產(chǎn)生的相互制約關(guān)系,這類線程間的主要問(wèn)題是互斥和死鎖問(wèn)題,這類關(guān)系被稱為互斥關(guān)系。典型的例子是“售票廳售票應(yīng)用”。售票

37、廳剩余100張票,10個(gè)窗口去賣這些票。這10個(gè)窗口,就是10條線程,售票廳就是他們共同操作的資源,其中剩余的100張票就是這個(gè)資源的一個(gè)狀態(tài)。線程買票的過(guò)程就是去遞減這個(gè)剩余數(shù)量的過(guò)程,需要對(duì)統(tǒng)一資源進(jìn)行控制訪問(wèn)。Java提供了關(guān)鍵字synchronized來(lái)進(jìn)行互斥操作的控制。11.3.6同步關(guān)鍵字synchronized 同步線程就是讓多個(gè)線程協(xié)調(diào)地工作。線程的同步保護(hù)了共享的數(shù)據(jù),使線程的控制和切換能同步執(zhí)行。 Java的多線程機(jī)制提供了關(guān)鍵詞synchronized來(lái)聲明同步方法。一個(gè)類中的任何方法都可聲明為synchronized。當(dāng)某對(duì)象用synchronized修飾時(shí),表明該對(duì)

38、象在同一時(shí)刻只能由一個(gè)線程訪問(wèn)。當(dāng)一個(gè)線程進(jìn)入聲明為synchronized的方法后,就能保證在任何其他線程訪問(wèn)這個(gè)方法之前完成自己各條命令的執(zhí)行。換言之,如果某一線程試圖訪問(wèn)一個(gè)已經(jīng)啟動(dòng)的synchronized方法,則這個(gè)線程必須等待,直到啟動(dòng)的線程執(zhí)行完畢。鎖定一個(gè)對(duì)象和一段代碼聲明格式為:鎖定一個(gè)方法聲明格式為:【例11-5】 一個(gè)最簡(jiǎn)單的同步實(shí)例解題思路:該例運(yùn)行結(jié)果顯示“1 0”或“0 1”,則表明實(shí)現(xiàn)同步,如果沒(méi)有同步,顯示“1 1”。代碼:程序SyncExample.javaublic class SyncExample private static Object lockOb

39、ject=new Object(); private static class Thread1 extends Threadint x,y;public void run() synchronized(lockObject)x=0; y=0; System.out.println(x); private static class Thread2 extends Threadint x,y;public void run()synchronized(lockObject)x=1; y=1; System.out.println(y); public static void main(String

40、 args) new Thread1().run(); new Thread2().run(); 運(yùn)行結(jié)果如下:11.3.7生產(chǎn)者與消費(fèi)者模型 生產(chǎn)者-消費(fèi)者(producer-consumer)問(wèn)題,也稱作有界緩沖區(qū)(bounded-buffer)問(wèn)題,兩個(gè)進(jìn)程共享一個(gè)公共的固定大小的緩沖區(qū)。其中一個(gè)是生產(chǎn)者,用于將消息放入緩沖區(qū);另外一個(gè)是消費(fèi)者,用于從緩沖區(qū)中取出消息。問(wèn)題出現(xiàn)在當(dāng)緩沖區(qū)已經(jīng)滿了,而此時(shí)生產(chǎn)者還想向其中放入一個(gè)新的數(shù)據(jù)項(xiàng)的情形,其解決方法是讓生產(chǎn)者此時(shí)進(jìn)行休眠,等待消費(fèi)者從緩沖區(qū)中取走了一個(gè)或者多個(gè)數(shù)據(jù)后再去喚醒它。同樣地,當(dāng)緩沖區(qū)已經(jīng)空了,而消費(fèi)者還想去取消息,此時(shí)也可

41、以讓消費(fèi)者進(jìn)行休眠,等待生產(chǎn)者放入一個(gè)或者多個(gè)數(shù)據(jù)時(shí)再喚醒它。【例11-6】 定義公共資源類解題思路:代碼中的變量number是保存的公共數(shù)據(jù)。并且定義兩個(gè)方法increace()和decreace()分別增加number的值和減少number的值。由于多線程的原因,必須加上synchronized關(guān)鍵字,注意while判斷的條件。代碼:程序PublicResource.java public class PublicResource / 公共資源類 private int number = 0; public synchronized void increace() / 增加公共資源 whi

42、le (number != 0) Try wait(); catch (InterruptedException e) e.printStackTrace(); number+; System.out.println(number); notify(); public synchronized void decreace() / 減少公共資源 while (number = 0) try wait(); catch (InterruptedException e) e.printStackTrace(); number-; System.out.println(number); notify(

43、); 【例11-7】 定義生產(chǎn)者線程和消費(fèi)者線程解題思路:程序ProducerThread.java和ConsumerThread.java定義了生產(chǎn)者線程和消費(fèi)者線程,并模擬多次生產(chǎn)和消費(fèi),即增加和減少公共資源的number值。代碼:程序ProducerThread.javapublic class ProducerThread implements Runnable /生產(chǎn)者線程,負(fù)責(zé)生產(chǎn)公共資源 private PublicResource resource; public ProducerThread(PublicResource resource) this.resource = r

44、esource; public void run() for (int i = 0; i 10; i+) try Thread.sleep(long) (Math.random() * 1000); catch (InterruptedException e) e.printStackTrace(); resource.increace(); 代碼:程序ConsumerThread.java public class ConsumerThread implements Runnable /消費(fèi)者線程,負(fù)責(zé)消費(fèi)公共資源 private PublicResource resource; publi

45、c ConsumerThread(PublicResource resource) this.resource = resource; public void run() for (int i = 0; i 10; i+) try Thread.sleep(long) (Math.random() * 1000); catch (InterruptedException e) e.printStackTrace(); resource.decreace(); /* * 生產(chǎn)者線程,負(fù)責(zé)生產(chǎn)公共資源 */public class ProducerThread implements Runnabl

46、e private PublicResource resource;public ProducerThread(PublicResource resource) this.resource = resource;Overridepublic void run() for (int i = 0; i 10; i+) try Thread.sleep(long) (Math.random() * 1000); catch (InterruptedException e) e.printStackTrace();resource.increace();/* * 消費(fèi)者線程,負(fù)責(zé)消費(fèi)公共資源 */pu

47、blic class ConsumerThread implements Runnable private PublicResource resource;public ConsumerThread(PublicResource resource) this.resource = resource;Overridepublic void run() for (int i = 0; i 10; i+) try Thread.sleep(long) (Math.random() * 1000); catch (InterruptedException e) e.printStackTrace();

48、resource.decreace();11.3.8案例2:模擬多個(gè)生產(chǎn)者和消費(fèi)者操作公共資源解題思路:模擬多個(gè)生產(chǎn)者和消費(fèi)者操作公共資源的情形,結(jié)果須保證是在允許范圍內(nèi)。代碼:ProducerConsumerTest.javapublic class ProducerConsumerTest public static void main(String args) PublicResource resource = new PublicResource(); new Thread(new ProducerThread(resource).start(); new Thread(new Con

49、sumerThread(resource).start(); new Thread(new ProducerThread(resource).start(); new Thread(new ConsumerThread(resource).start(); new Thread(new ProducerThread(resource).start(); new Thread(new ConsumerThread(resource).start(); 運(yùn)行結(jié)果如下:11. 4 實(shí)訓(xùn):龜兔賽跑【任務(wù)目的】掌握線程創(chuàng)建啟動(dòng),使用Runnable接口【任務(wù)要求】龜兔賽跑的多線程程序。 兔子比烏龜快5倍,但休息的時(shí)間長(zhǎng)10倍。【解題

溫馨提示

  • 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ì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論