版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第9章多線程9.1進(jìn)程與線程
9.2認(rèn)識(shí)線程
9.3線程的狀態(tài)
9.4線程操作的一些方法
9.1進(jìn)?程?與?線?程
進(jìn)程是程序的一次動(dòng)態(tài)執(zhí)行過(guò)程,它經(jīng)歷了從代碼加載、執(zhí)行到執(zhí)行完畢的一個(gè)完整過(guò)程。這個(gè)過(guò)程也是進(jìn)程本身從產(chǎn)生、發(fā)展到最終消亡的過(guò)程。多進(jìn)程操作系統(tǒng)能同時(shí)運(yùn)行多個(gè)進(jìn)程(程序),由于CPU具備分時(shí)機(jī)制,所以每個(gè)進(jìn)程都能循環(huán)獲得自己的CPU時(shí)間片。由于CPU執(zhí)行速度非???,使得所有程序好像是在“同時(shí)”運(yùn)行一樣。
線程是比進(jìn)程更小的執(zhí)行單位,線程是進(jìn)程內(nèi)部單一的一個(gè)順序控制流。所謂多線程,是指一個(gè)進(jìn)程在執(zhí)行過(guò)程中可以產(chǎn)生多個(gè)線程,這些線程可以同時(shí)存在、同時(shí)運(yùn)行,形成多條執(zhí)行線索。一個(gè)進(jìn)程可能包含了多個(gè)同時(shí)執(zhí)行的線程。
多線程是實(shí)現(xiàn)并發(fā)機(jī)制的一種有效手段。進(jìn)程和線程一樣,都是實(shí)現(xiàn)并發(fā)的一個(gè)基本單位。
線程和進(jìn)程的主要差別體現(xiàn)在以下兩個(gè)方面:
(1)同樣作為基本的執(zhí)行單元,線程是劃分得比進(jìn)程更小的執(zhí)行單位。
(2)每個(gè)進(jìn)程都有一段專(zhuān)用的內(nèi)存區(qū)域。與此相反,線程卻共享內(nèi)存單元(包括代碼和數(shù)據(jù)),通過(guò)共享的內(nèi)存單元來(lái)實(shí)現(xiàn)數(shù)據(jù)交換、實(shí)時(shí)通信與必要的同步操作。多線程的應(yīng)用范圍很廣。在一般情況下,程序的某些部分同特定的事件或資源聯(lián)系在一起,同時(shí)又不想為它暫停程序其他部分的執(zhí)行,在這種情況下,就可以考慮創(chuàng)建一個(gè)線程,令它與那個(gè)事件或資源關(guān)聯(lián)到一起,并讓其獨(dú)立于主程序運(yùn)行。通過(guò)使用線程,可以避免用戶在運(yùn)行程序和得到結(jié)果之間停頓,還可以讓一些任務(wù)(如打印任務(wù))在后臺(tái)運(yùn)行,用戶則在前臺(tái)繼續(xù)完成其他工作。總之,利用多線程技術(shù),編程人員可以方便地開(kāi)發(fā)出能同時(shí)處理多個(gè)任務(wù)的功能強(qiáng)大的應(yīng)用程序。
9.2認(rèn)識(shí)線程
在傳統(tǒng)的程序語(yǔ)言中,運(yùn)行的順序總是必須順著程序的流程來(lái)走,遇到if-else語(yǔ)句就加以判斷,遇到for、while等循環(huán)會(huì)多繞幾個(gè)圈,最后程序還是按著一定的程序走,且一次只能運(yùn)行一個(gè)程序塊。Java的“多線程”打破了這種傳統(tǒng)的束縛。例如,有些包含循環(huán)的線程可能要使用比較長(zhǎng)的一段時(shí)間來(lái)運(yùn)算,此時(shí)便可讓另一個(gè)線程來(lái)做其他處理。本節(jié)將用一個(gè)簡(jiǎn)單的程序來(lái)說(shuō)明單一線程與多線程的區(qū)別。ThreadDemo9_1是單一線程的范例,其程序代碼編寫(xiě)方法與前幾節(jié)的程序代碼并沒(méi)有什么不同。
【例9-1】ThreadDemo9_1.java。程序說(shuō)明:
(1)第15~21行定義了run()方法,用循環(huán)輸出10個(gè)連續(xù)的字符串。
(2)第5行創(chuàng)建TestThread對(duì)象之后調(diào)用run()方法,輸出“TestThread在運(yùn)行”,最后執(zhí)行main()方法中的循環(huán),輸出“main線程在運(yùn)行”。
從本例中可看出,要想運(yùn)行main()方法中的循環(huán),必須要等TestThread類(lèi)中的run()方法執(zhí)行完之后才可以運(yùn)行,這便是單一線程的缺陷。在Java中,是否可以同時(shí)運(yùn)行第9行與第19行的語(yǔ)句,使得“main線程在運(yùn)行”和“TestThread在運(yùn)行”交錯(cuò)輸出呢?答案是肯定的,其方法是:在Java中激活多個(gè)線程。那么,該如何激活線程呢?如果在類(lèi)里要激活線程,必須先做好下面兩個(gè)準(zhǔn)備:
(1)線程必須擴(kuò)展自Thread類(lèi),使自己成為它的子類(lèi)。
(2)線程的處理必須編寫(xiě)在run()方法內(nèi)。
9.2.1通過(guò)繼承Thread類(lèi)實(shí)現(xiàn)多線程
Thread存放在java.lang類(lèi)庫(kù)中,但并不需加載java.lang類(lèi)庫(kù),因?yàn)樗鼤?huì)自動(dòng)加載。此外,run()方法是定義在Thread類(lèi)中的一個(gè)方法,因此把線程的程序代碼編寫(xiě)在run()方法內(nèi),事實(shí)上所做的就是覆蓋操作。因此要使一個(gè)類(lèi)可激活線程,必須按照下面的語(yǔ)法來(lái)編寫(xiě):從運(yùn)行結(jié)果中可以發(fā)現(xiàn),兩行輸出是交替進(jìn)行的。也就是說(shuō),程序是采用多線程機(jī)制運(yùn)行的。與之前的程序相比,修改后的程序第13行TestThread類(lèi)繼承了Thread類(lèi),第5行調(diào)用的不再是run()方法,而是start()方法。所以,要啟動(dòng)線程,必須調(diào)用Thread類(lèi)之中的start()方法,而調(diào)用了start()方法,也就是調(diào)用了run()方法。
9.2.2通過(guò)實(shí)現(xiàn)Runnable接口實(shí)現(xiàn)多線程
Java程序只允許單一繼承,即一個(gè)子類(lèi)只能有一個(gè)父類(lèi),所以在Java中如果一個(gè)類(lèi)繼承了某一個(gè)類(lèi),同時(shí)又想采用多線程技術(shù),就不能用Thread類(lèi)產(chǎn)生線程,因?yàn)镴ava不允許多繼承,這時(shí)就要用Runnable接口來(lái)創(chuàng)建線程。程序說(shuō)明:
(1)第5行實(shí)例化一個(gè)TestThread類(lèi)的對(duì)象。
(2)第6行通過(guò)TestThread類(lèi)(Runnable接口的子類(lèi))去實(shí)例化一個(gè)Thread類(lèi)的對(duì)象,之后調(diào)用start()方法啟動(dòng)多線程。
(3)第14行TestThread類(lèi)實(shí)現(xiàn)了Runnable接口,同時(shí)復(fù)寫(xiě)了Runnable接口之中的run()方法。也就是說(shuō),此類(lèi)為一多線程實(shí)現(xiàn)類(lèi)。
從輸出結(jié)果可以發(fā)現(xiàn),無(wú)論繼承了Thread類(lèi)還是實(shí)現(xiàn)了Runnable接口,運(yùn)行結(jié)果都是一樣的。為什么實(shí)現(xiàn)了Runnable接口還需要調(diào)用Thread類(lèi)中的start()方法才能啟動(dòng)多線程呢?通過(guò)查找JDK文檔就可以發(fā)現(xiàn),在Runnable接口中只有一個(gè)run()方法,如圖9-1所示。圖9-1Runnable接口中的方法列表從圖9-1中可以看出,在Runnable接口中并沒(méi)有start()方法,所以一個(gè)類(lèi)實(shí)現(xiàn)了Runnable接口也必須用Thread類(lèi)中的start()方法來(lái)啟動(dòng)多線程。這點(diǎn)可以通過(guò)查找JDK文檔中的Thread類(lèi)知道。在Thread類(lèi)之中,有這樣一個(gè)構(gòu)造方法:
publicThread(Runnabletarget)
由此構(gòu)造方法可以看出,可以將一個(gè)Runnable接口的實(shí)例化對(duì)象作為參數(shù)去實(shí)例化Thread類(lèi)對(duì)象。在實(shí)際開(kāi)發(fā)中,希望讀者盡可能使用Runnable接口去實(shí)現(xiàn)多線程機(jī)制。9.2.3兩種多線程實(shí)現(xiàn)機(jī)制的比較
由9.2.1節(jié)和9.2.2節(jié)可以看出,不管實(shí)現(xiàn)了Runnable接口還是繼承了Thread類(lèi)其結(jié)果都是一樣的,那么這兩者之間有什么關(guān)系呢?讀者可以通過(guò)查看JDK文檔發(fā)現(xiàn)二者之間的聯(lián)系,如圖9-2所示。圖9-2Thread類(lèi)與Runnable接口的關(guān)系從圖9-2中可以看出,Thread類(lèi)實(shí)現(xiàn)了Runnable接口。也就是說(shuō),Thread類(lèi)也是Runnable接口的一個(gè)子類(lèi)。那么兩者之間除了這些聯(lián)系之外還有什么區(qū)別呢?下面通過(guò)編寫(xiě)一個(gè)應(yīng)用程序來(lái)進(jìn)行比較分析。下面程序是一個(gè)模擬鐵路售票系統(tǒng)的范例,實(shí)現(xiàn)四個(gè)售票點(diǎn)發(fā)售某日某次列車(chē)的車(chē)票20張,一個(gè)售票點(diǎn)用一個(gè)線程來(lái)表示。
【例9-4】ThreadDemo9_3.java。下例由ThreadDemo9_3的程序修改而成,這里讓main()方法中產(chǎn)生四個(gè)線程。
【例9-5】
修改后的ThreadDemo9_3.java。由于程序的輸出結(jié)果過(guò)長(zhǎng),所以只截取了后面一部分,但從這部分輸出結(jié)果中可以看出,這里啟動(dòng)了四個(gè)線程對(duì)象,這四個(gè)線程對(duì)象各自占有各自的資源,所以可以得出結(jié)論:用Thread類(lèi)實(shí)際上無(wú)法達(dá)到資源共享的目的。
那么實(shí)現(xiàn)Runnable接口會(huì)如何呢?下面這個(gè)例子也修改自ThreadDemo9_3,讀者可以觀察一下輸出結(jié)果。
【例9-6】ThreadDemo9_4.java。從上面的程序中可以看出,第7行到第10行啟動(dòng)了四個(gè)線程,從程序的輸出結(jié)果來(lái)看,盡管啟動(dòng)了四個(gè)線程對(duì)象,但是結(jié)果都操縱了同一個(gè)資源,實(shí)現(xiàn)了資源共享的目的。
可見(jiàn),實(shí)現(xiàn)Runnable接口相對(duì)于繼承Thread類(lèi)來(lái)說(shuō)具有如下顯著優(yōu)勢(shì):
(1)適合多個(gè)相同程序代碼的線程去處理同一資源的情況,把虛擬CPU(線程)同程序的代碼、數(shù)據(jù)有效分離,較好地體現(xiàn)了面向?qū)ο蟮脑O(shè)計(jì)思想。
(2)可以避免由于Java的單繼承特性帶來(lái)的局限。開(kāi)發(fā)中經(jīng)常碰到這樣一種情況,即:當(dāng)要將已經(jīng)繼承了某一個(gè)類(lèi)的子類(lèi)放入多線程中時(shí),由于一個(gè)類(lèi)不能同時(shí)有兩個(gè)父類(lèi),所以不能用繼承Thread類(lèi)的方式,那么就只能采用實(shí)現(xiàn)Runnable接口的方式。
(3)增強(qiáng)了程序的健壯性,代碼能夠被多個(gè)線程共享,代碼與數(shù)據(jù)是獨(dú)立的。當(dāng)多個(gè)線程的執(zhí)行代碼來(lái)自同一個(gè)類(lèi)的實(shí)例時(shí),稱(chēng)它們共享相同的代碼。多個(gè)線程可以操作相同的數(shù)據(jù),與它們的代碼無(wú)關(guān)。當(dāng)共享訪問(wèn)相同的對(duì)象時(shí),共享相同的數(shù)據(jù)。當(dāng)線程被構(gòu)造時(shí),需要的代碼和數(shù)據(jù)通過(guò)一個(gè)對(duì)象作為構(gòu)造函數(shù)實(shí)參傳遞進(jìn)去,這個(gè)對(duì)象就是一個(gè)實(shí)現(xiàn)了Runnable接口的類(lèi)的實(shí)例。
事實(shí)上,幾乎所有多線程應(yīng)用都可用第二種方式,即實(shí)現(xiàn)Runnable接口。
9.3線?程?的?狀?態(tài)
每個(gè)Java程序都有一個(gè)缺省的主線程。對(duì)于Java應(yīng)用程序,主線程是main()方法執(zhí)行的線索;對(duì)于Applet程序,主線程是指揮瀏覽器加載并執(zhí)行JavaApplet程序的線索。要想實(shí)現(xiàn)多線程,必須在主線程中創(chuàng)建新的線程對(duì)象。任何線程一般具有五種狀態(tài),即創(chuàng)建、就緒、運(yùn)行、阻塞、終止。線程狀態(tài)的轉(zhuǎn)移與方法之間的關(guān)系可用圖9-3來(lái)表示。圖9-3線程的狀態(tài)轉(zhuǎn)換
1.創(chuàng)建狀態(tài)
在程序中用構(gòu)造方法創(chuàng)建了一個(gè)線程對(duì)象后,新的線程對(duì)象便處于新建狀態(tài),此時(shí)它已經(jīng)有了相應(yīng)的內(nèi)存空間和其他資源,但還處于不可運(yùn)行狀態(tài)。創(chuàng)建一個(gè)線程對(duì)象可采用線程構(gòu)造方法來(lái)實(shí)現(xiàn),如Threadthread=newThread();。
2.就緒狀態(tài)
創(chuàng)建線程對(duì)象后,調(diào)用該線程的start()方法就可以啟動(dòng)線程。當(dāng)線程啟動(dòng)時(shí),線程進(jìn)入就緒狀態(tài)。此時(shí),線程將進(jìn)入線程隊(duì)列排隊(duì),等待CPU服務(wù),這表明它已經(jīng)具備了運(yùn)行
條件。
3.運(yùn)行狀態(tài)
當(dāng)就緒狀態(tài)的線程被調(diào)用并獲得處理器資源時(shí),線程就進(jìn)入了運(yùn)行狀態(tài)。此時(shí),自動(dòng)調(diào)用該線程對(duì)象的run()方法。run()方法定義了該線程的操作和功能。
4.阻塞狀態(tài)
一個(gè)正在執(zhí)行的線程在某些特殊情況下,如被人為掛起或需要執(zhí)行耗時(shí)的輸入/輸出操作時(shí),將讓出CPU并暫時(shí)中止自己的執(zhí)行,進(jìn)入阻塞狀態(tài)。在可執(zhí)行狀態(tài)下,如果調(diào)用sleep()、suspend()、wait()等方法,線程都將進(jìn)入阻塞狀態(tài)。阻塞時(shí),線程不能進(jìn)入排隊(duì)隊(duì)列,只有當(dāng)引起阻塞的原因被消除后,線程才可以轉(zhuǎn)入就緒狀態(tài)。
5.終止?fàn)顟B(tài)
線程調(diào)用stop()方法時(shí)或run()方法執(zhí)行結(jié)束后,線程即處于終止?fàn)顟B(tài)。處于終止?fàn)顟B(tài)的線程不具有繼續(xù)運(yùn)行的能力。
9.4線程操作的一些方法
在Java實(shí)現(xiàn)多線程的程序中,雖然Thread類(lèi)實(shí)現(xiàn)了Runnable接口,但是操作線程的主要方法并不在Runnable接口中,而是在Thread類(lèi)中。表9-1列出了Thread類(lèi)中的主要方法。表9-1Thread類(lèi)中的主要方法9.4.1取得和設(shè)置線程的名稱(chēng)
在Thread類(lèi)中,可以通過(guò)getName()方法取得線程的名稱(chēng),通過(guò)setName()方法設(shè)置線程的名稱(chēng)。線程的名稱(chēng)一般在啟動(dòng)線程前設(shè)置,但也允許為已經(jīng)運(yùn)行的線程設(shè)置名稱(chēng)。允許兩個(gè)Thread對(duì)象有相同的名字,但為了清晰,應(yīng)該盡量避免這種情況的發(fā)生。
另外,如果程序并沒(méi)有為線程指定名稱(chēng),則系統(tǒng)會(huì)自動(dòng)為線程分配一個(gè)名稱(chēng)。
【例9-7】GetNameThreadDemo.java。程序說(shuō)明:
(1)第1行聲明一個(gè)GetNameThreadDemo類(lèi),此類(lèi)繼承自Thread類(lèi),之后3~7行復(fù)寫(xiě)Thread類(lèi)中的run()方法。
(2)第8~14行聲明一個(gè)printMsg()方法,此方法用于取得當(dāng)前線程的信息。在第11行,通過(guò)Thread類(lèi)中的currentThread()方法,返回一個(gè)Thread類(lèi)的實(shí)例化對(duì)象。由表9-1可知,此方法返回當(dāng)前正在運(yùn)行的線程,即返回正在調(diào)用此方法的線程。第12行通過(guò)調(diào)用Thread類(lèi)中的getName()方法,返回當(dāng)前運(yùn)行線程的名稱(chēng)。
(3)第6行和第21行分別調(diào)用了printMsg()方法,但第6行從多線程的run()方法中調(diào)用,而第21行從main()方法中調(diào)用。為什么程序中輸出的運(yùn)行線程的名稱(chēng)中會(huì)有一個(gè)main呢?這是因?yàn)閙ain()方法也是一個(gè)線程,實(shí)際上在命令行中運(yùn)行Java命令時(shí),就啟動(dòng)了一個(gè)JVM的進(jìn)程,默認(rèn)情況下此進(jìn)程會(huì)產(chǎn)生兩個(gè)線程:一個(gè)是main()方法線程,另外一個(gè)就是垃圾回收(GC)線程。
下例介紹如何在線程中設(shè)置線程的名稱(chēng)。
【例9-8】SetNameThreadDemo.java。9.4.2線程是否啟動(dòng)的判斷
通過(guò)Thread類(lèi)中的start()方法通知線程規(guī)劃器這個(gè)新線程已準(zhǔn)備就緒,而且應(yīng)當(dāng)在規(guī)劃器的最早方便時(shí)間調(diào)用它的run()方法。在程序中也可以通過(guò)isAlive()方法來(lái)測(cè)試線程是否已經(jīng)啟動(dòng)而且仍然在啟動(dòng)。
【例9-9】StartThreadDemo.java。程序說(shuō)明:
(1)第20行在線程運(yùn)行之前調(diào)用isAlive()方法,判斷線程是否啟動(dòng),但在此處并沒(méi)有啟動(dòng),所以返回“false”,表示線程未啟動(dòng)。
(2)第22行在啟動(dòng)線程之后調(diào)用isAlive()方法,此時(shí)線程已經(jīng)啟動(dòng),所以返回“true”。
(3)第28行在main()方法快結(jié)束時(shí)調(diào)用isAlive()方法,此時(shí)的狀態(tài)不再固定,有可能是true,也有可能是false。9.4.3后臺(tái)線程與setDaemon()方法
對(duì)Java程序來(lái)說(shuō),只要還有一個(gè)前臺(tái)線程在運(yùn)行,這個(gè)進(jìn)程就不會(huì)結(jié)束;如果一個(gè)進(jìn)程中只有后臺(tái)線程在運(yùn)行,這個(gè)進(jìn)程就會(huì)結(jié)束。前臺(tái)線程是相對(duì)于后臺(tái)線程而言的,前面所介紹的線程都是前臺(tái)線程。那么什么樣的線程是后臺(tái)線程呢?如果某個(gè)線程對(duì)象在啟動(dòng)(調(diào)用start()方法)之前調(diào)用了setDaemon(true)
溫馨提示
- 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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 產(chǎn)品安裝及質(zhì)量保障措施
- 施工安全與環(huán)境保護(hù)
- 二零二五年度鋼結(jié)構(gòu)建筑室內(nèi)裝修材料供應(yīng)合同3篇
- 人教版三年級(jí)上冊(cè)語(yǔ)文30一次成功實(shí)驗(yàn)課件
- 2024年海南衛(wèi)生健康職業(yè)學(xué)院高職單招職業(yè)技能測(cè)驗(yàn)歷年參考題庫(kù)(頻考版)含答案解析
- 2024年海南體育職業(yè)技術(shù)學(xué)院高職單招數(shù)學(xué)歷年參考題庫(kù)含答案解析
- 2024年浙江電力職業(yè)技術(shù)學(xué)院高職單招職業(yè)技能測(cè)驗(yàn)歷年參考題庫(kù)(頻考版)含答案解析
- 2024年浙江汽車(chē)職業(yè)技術(shù)學(xué)院高職單招職業(yè)技能測(cè)驗(yàn)歷年參考題庫(kù)(頻考版)含答案解析
- 幼兒園午托服務(wù)項(xiàng)目方案
- 2024年浙江農(nóng)業(yè)商貿(mào)職業(yè)學(xué)院高職單招職業(yè)適應(yīng)性測(cè)試歷年參考題庫(kù)含答案解析
- 合同法課件 教學(xué)課件
- 合規(guī)管理規(guī)定(新設(shè)合規(guī)部)、國(guó)有企業(yè)合規(guī)管理辦法
- 2024-2030年中國(guó)銫原子鐘行業(yè)市場(chǎng)發(fā)展趨勢(shì)與前景展望戰(zhàn)略分析報(bào)告
- 肌內(nèi)注射操作并發(fā)癥的預(yù)防及處理
- 人工智能導(dǎo)論智慧樹(shù)知到期末考試答案章節(jié)答案2024年哈爾濱工程大學(xué)
- 收費(fèi)站綠通車(chē)培訓(xùn)
- 會(huì)陰痛個(gè)案護(hù)理
- 小學(xué)音樂(lè)一年級(jí)下冊(cè)放牛歌音樂(lè)教案
- 門(mén)診部運(yùn)營(yíng)方案
- 血友病的家庭護(hù)理
- 統(tǒng)編版六年級(jí)語(yǔ)文上冊(cè)專(zhuān)項(xiàng) 專(zhuān)題09病句辨析與修改-原卷版+解析
評(píng)論
0/150
提交評(píng)論