JAVA多線程編程詳解-詳細(xì)操作例子_第1頁
JAVA多線程編程詳解-詳細(xì)操作例子_第2頁
JAVA多線程編程詳解-詳細(xì)操作例子_第3頁
JAVA多線程編程詳解-詳細(xì)操作例子_第4頁
JAVA多線程編程詳解-詳細(xì)操作例子_第5頁
已閱讀5頁,還剩10頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

1、一、理解多線程多線程是這樣一種機(jī)制,它允許在程序中并發(fā)執(zhí)行多個指令流,每個指令流都稱為一個線程,彼此間互相獨立。線程又稱為輕量級進(jìn)程,它和進(jìn)程一樣擁有獨立的執(zhí)行控制,由操作系統(tǒng)負(fù)責(zé)調(diào)度,區(qū)別在于線程沒有獨立的存儲空間,而是和所屬進(jìn)程中的其它線程共享一個存儲空間,這使得線程間的通信遠(yuǎn)較進(jìn)程簡單。具體到j(luò)ava內(nèi)存模型,由于Java被設(shè)計為跨平臺的語言,在內(nèi)存管理上,顯然也要有一個統(tǒng)一的模型。系統(tǒng)存在一個主內(nèi)存(Main Memory,Java中所有變量都儲存在主存中,對于所有線程都是共享的。每條線程都有自己的工作內(nèi)存(Working Memory,工作內(nèi)存中保存的是主存中某些變量的拷貝,線程對所

2、有變量的操作都是在工作內(nèi)存中進(jìn)行,線程之間無法相互直接訪問,變量傳遞均需要通過主存完成。多個線程的執(zhí)行是并發(fā)的,也就是在邏輯上“同時”,而不管是否是物理上的“同時”。如果系統(tǒng)只有一個CPU,那么真正的“同時”是不可能的。多線程和傳統(tǒng)的單線程在程序設(shè)計上最大的區(qū)別在于,由于各個線程的控制流彼此獨立,使得各個線程之間的代碼是亂序執(zhí)行的,將會帶來線程調(diào)度,同步等問題。二、在Java中實現(xiàn)多線程我們不妨設(shè)想,為了創(chuàng)建一個新的線程,我們需要做些什么?很顯然,我們必須指明這個線程所要執(zhí)行的代碼,而這就是在Java中實現(xiàn)多線程我們所需要做的一切!那么如何提供給Java 我們要線程執(zhí)行的代碼呢?讓我們來看一看

3、Thread 類。Thread 類最重要的方法是run (,它為Thread 類的方法start(所調(diào)用,提供我們的線程所要執(zhí)行的代碼。為了指定我們自己的代碼,只需要覆蓋它!方法一:繼承Thread 類,重寫方法run(,我們在創(chuàng)建Thread 類的子類中重寫run(,加入線程所要執(zhí)行的代碼即可。下面是一個例子:public class TwoThread extends Thread public void run( for ( int i = 0; i < 10; i+ public static void main(String args TwoThread tt = new Tw

4、oThread(;tt.start(;for ( int i = 0; i < 10; i+ 這種方法簡單明了,符合大家的習(xí)慣,但是,它也有一個很大的缺點,那就是如果我們的類已經(jīng)從一個類繼承,則無法再繼承Thread 類。方法二:實現(xiàn)Runnable 接口Runnable 接口只有一個方法run(,我們聲明自己的類實現(xiàn)Runnable 接口并提供這一方法,將我們的線程代碼寫入其中,就完成了這一部分的任務(wù)。但是Runnable 接口并沒有任何對線程的支持,我們還必須創(chuàng)建Thread 類的實例,這一點通過Thread 類的構(gòu)造函數(shù)public Thread (Runnable target;

5、來實現(xiàn)。下面是一個例子:public class MyThread implements Runnable int count=1, number;public MyThread(int num number = num;public void run( /實現(xiàn)了接口的run(方法while(true if(+count= 6 return;public static void main(String args for(int i = 0; i < 5; i+new Thread(new MyThread(i+1.start(;使用Runnable 接口來實現(xiàn)多線程使得我們能夠在一個類中

6、包容所有的代碼,有利于封裝下面讓我們一起來研究一下多線程使用中的一些問題。三、線程的四種狀態(tài)1、新狀態(tài):線程已被創(chuàng)建但尚未執(zhí)行(start(尚未被調(diào)用。2、可執(zhí)行狀態(tài):線程可以執(zhí)行,雖然不一定正在執(zhí)行。CPU 時間隨時可能被分配給該線程,從而使得它執(zhí)行。3、阻塞狀態(tài):線程不會被分配CPU 時間,無法執(zhí)行;可能阻塞于I/O,或者阻塞于同步鎖。4、死亡狀態(tài):正常情況下run(返回使得線程死亡。調(diào)用stop(或destroy(亦有同樣效果,但是不被推薦,前者會產(chǎn)生異常,后者是強(qiáng)制終止,不會釋放鎖。四、線程的優(yōu)先級線程的優(yōu)先級代表該線程的重要程度,當(dāng)有多個線程同時處于可執(zhí)行狀態(tài)并等待獲得CPU 時間時

7、,線程調(diào)度系統(tǒng)根據(jù)各個線程的優(yōu)先級來決定給誰分配CPU 時間,優(yōu)先級高的線程有更大的機(jī)會獲得CPU 時間,優(yōu)先級低的線程也不是沒有機(jī)會,只是機(jī)會要小一些罷了。你可以調(diào)用Thread 類的方法getPriority(和setPriority(來存取線程的優(yōu)先級,線程的優(yōu)先級界于1 (MIN_PRIORITY和10(MAX_PRIORITY之間,缺省是5(NORM_PRIORITY。狀態(tài)圖1狀態(tài)圖2五、線程的同步由于同一進(jìn)程的多個線程共享同一片存儲空間,在帶來方便的同時,也帶來了訪問沖突這個嚴(yán)重的問題。Java語言提供了專門機(jī)制以解決這種沖突,有效避免了同一個數(shù)據(jù)對象被多個線程同時訪問。我們只需針

8、對方法提出一套機(jī)制,這套機(jī)制就是synchronized關(guān)鍵字,它包括兩種用法:synchronized 方法和synchronized 塊。1.synchronized 方法:通過在方法聲明中加入synchronized關(guān)鍵字來聲明synchronized 方法。synchronized 方法控制對類成員變量的訪問:每個類實例對應(yīng)一把鎖,每個synchronized 方法都必須獲得調(diào)用該方法的類實例的鎖方能執(zhí)行,否則所屬線程阻塞,方法一旦執(zhí)行,就獨占該鎖,直到從該方法返回時才將鎖釋放,此后被阻塞的線程方能獲得該鎖,重新進(jìn)入可執(zhí)行狀態(tài)。這種機(jī)制確保了同一時刻對于每一個類實例,其所有聲明為syn

9、chronized 的成員函數(shù)中至多只有一個處于可執(zhí)行狀態(tài)(因為至多只有一個能夠獲得該類實例對應(yīng)的鎖,從而有效避免了類成員變量的訪問沖突(只要所有可能訪問類成員變量的方法均被聲明為synchronized。在Java 中,不光是類實例,每一個類也對應(yīng)一把鎖,這樣我們也可將類的靜態(tài)成員函數(shù)聲明為synchronized,以控制其對類的靜態(tài)成員變量的訪問。synchronized 方法的缺陷:若將一個大的方法聲明為synchronized 將會大大影響效率,典型地,若將線程類的方法run(聲明為synchronized ,由于在線程的整個生命期內(nèi)它一直在運(yùn)行,因此將導(dǎo)致它對本類任何synchron

10、ized方法的調(diào)用都永遠(yuǎn)不會成功。2. synchronized 塊:通過synchronized關(guān)鍵字來聲明synchronized 塊。語法如下:synchronized(syncObject /允許訪問控制的代碼synchronized 塊是這樣一個代碼塊,其中的代碼必須獲得對象syncObject 的鎖方能執(zhí)行,具體機(jī)制同前所述。由于可以針對任意代碼塊,且可任意指定上鎖的對象,故靈活性較高。六、線程的阻塞為了解決對共享存儲區(qū)的訪問沖突,Java 引入了同步機(jī)制,現(xiàn)在讓我們來考察多個線程對共享資源的訪問,顯然同步機(jī)制已經(jīng)不夠了,因為在任意時刻所要求的資源不一定已經(jīng)準(zhǔn)備好了被訪問,反過來,

11、同一時刻準(zhǔn)備好了的資源也可能不止一個。為了解決這種情況下的訪問控制問題,Java 引入了對阻塞機(jī)制的支持。阻塞指的是暫停一個線程的執(zhí)行以等待某個條件發(fā)生(如某資源就緒。Java 提供了大量方法來支持阻塞,下面讓對它們逐一分析。1. sleep(方法:sleep(允許指定以毫秒為單位的一段時間作為參數(shù),它使得線程在指定的時間內(nèi)進(jìn)入阻塞狀態(tài),不能得到CPU 時間,指定的時間一過,線程重新進(jìn)入可執(zhí)行狀態(tài)。典型地,sleep(被用在等待某個資源就緒的情形:測試發(fā)現(xiàn)條件不滿足后,讓線程阻塞一段時間后重新測試,直到條件滿足為止。2. suspend(和resume(方法:兩個方法配套使用, suspend

12、(使得線程進(jìn)入阻塞狀態(tài),并且不會自動恢復(fù),必須其對應(yīng)的resume(被調(diào)用,才能使得線程重新進(jìn)入可執(zhí)行狀態(tài)。典型地,suspend(和resume(被用在等待另一個線程產(chǎn)生的結(jié)果的情形:測試發(fā)現(xiàn)結(jié)果還沒有產(chǎn)生后,讓線程阻塞,另一個線程產(chǎn)生了結(jié)果后,調(diào)用resume (使其恢復(fù)。3. yield(方法:yield(使得線程放棄當(dāng)前分得的CPU 時間,但是不使線程阻塞,即線程仍處于可執(zhí)行狀態(tài),隨時可能再次分得CPU 時間。調(diào)用yield(的效果等價于調(diào)度程序認(rèn)為該線程已執(zhí)行了足夠的時間從而轉(zhuǎn)到另一個線程。4. wait(和notify(方法:兩個方法配套使用, wait(使得線程進(jìn)入阻塞狀態(tài),它有

13、兩種形式,一種允許指定以毫秒為單位的一段時間作為參數(shù),另一種沒有參數(shù),前者當(dāng)對應(yīng)的notify(被調(diào)用或者超出指定時間時線程重新進(jìn)入可執(zhí)行狀態(tài),后者則必須對應(yīng)的notify(被調(diào)用。2和4區(qū)別的核心在于,前面敘述的所有方法,阻塞時都不會釋放占用的鎖(如果占用了的話,而這一對方法則相反。上述的核心區(qū)別導(dǎo)致了一系列的細(xì)節(jié)上的區(qū)別。首先,前面敘述的所有方法都隸屬于Thread 類,但是這一對卻直接隸屬于Object 類,也就是說,所有對象都擁有這一對方法。因為這一對方法阻塞時要釋放占用的鎖,而鎖是任何對象都具有的,調(diào)用任意對象的wait(方法導(dǎo)致線程阻塞,并且該對象上的鎖被釋放。而調(diào)用任意對象的no

14、tify(方法則導(dǎo)致因調(diào)用該對象的wait(方法而阻塞的線程中隨機(jī)選擇的一個解除阻塞(但要等到獲得鎖后才真正可執(zhí)行。其次,前面敘述的所有方法都可在任何位置調(diào)用,但是這一對方法卻必須在synchronized 方法或塊中調(diào)用,理由也很簡單,只有在synchronized 方法或塊中當(dāng)前線程才占有鎖,才有鎖可以釋放。同樣的道理,調(diào)用這一對方法的對象上的鎖必須為當(dāng)前線程所擁有,這樣才有鎖可以釋放。因此,這一對方法調(diào)用必須放置在這樣的synchronized 方法或塊中,該方法或塊的上鎖對象就是調(diào)用這一對方法的對象。若不滿足這一條件,則程序雖然仍能編譯,但在運(yùn)行時會出現(xiàn)IllegalMonitorSt

15、ateException 異常。wait(和notify(方法的上述特性決定了它們經(jīng)常和synchronized 方法或塊一起使用,將它們和操作系統(tǒng)的進(jìn)程間通信機(jī)制作一個比較就會發(fā)現(xiàn)它們的相似性: synchronized方法或塊提供了類似于操作系統(tǒng)原語的功能,它們的結(jié)合用于解決各種復(fù)雜的線程間通信問題。關(guān)于wait(和notify(方法最后再說明兩點:第一:調(diào)用notify(方法導(dǎo)致解除阻塞的線程是從因調(diào)用該對象的wait(方法而阻塞的線程中隨機(jī)選取的,我們無法預(yù)料哪一個線程將會被選擇,所以編程時要特別小心,避免因這種不確定性而產(chǎn)生問題。第二:除了notify(,還有一個方法notifyAl

16、l(也可起到類似作用,唯一的區(qū)別在于,調(diào)用notifyAll(方法將把因調(diào)用該對象的wait(方法而阻塞的所有線程一次性全部解除阻塞。當(dāng)然,只有獲得鎖的那一個線程才能進(jìn)入可執(zhí)行狀態(tài)。談到阻塞,就不能不談一談死鎖,略一分析就能發(fā)現(xiàn), suspend(方法和不指定超時期限的wait(方法的調(diào)用都可能產(chǎn)生死鎖。遺憾的是,Java 并不在語言級別上支持死鎖的避免,我們在編程中必須小心地避免死鎖。以上我們對Java 中實現(xiàn)線程阻塞的各種方法作了一番分析,我們重點分析了wait(和notify(方法,因為它們的功能最強(qiáng)大,使用也最靈活,但是這也導(dǎo)致了它們的效率較低,較容易出錯。實際使用中我們應(yīng)該靈活使用各

17、種方法,以便更好地達(dá)到我們的目的。還有一個方法比較有用,Thread的join(方法可用于讓當(dāng)前線程阻塞,以等待特定線程的消亡。public class DemoThread implements Runnable public DemoThread( TestThread testthread1 = newTestThread(this, "1"TestThread testthread2 = newTestThread(this, "2"testthread2.start(;testthread1.start(;public static void

18、main(String args DemoThread demoThread1 = new DemoThread(; public void run( TestThread t = (TestThreadThread.currentThread(;try if(!t.getName(.equalsIgnoreCase("1" synchronized (this wait(;while (true if (t.getTime( % 2 = 0 synchronized (this notify(;if (t.getTime( = 10 break;wait(;catch (

19、Exception e e.printStackTrace(;class TestThread extends Thread private int time = 0;public TestThread(Runnable r, String name super(r, name;public int getTime( return time;public int increaseTime( return +time;運(yùn)行結(jié)time in thread1=1time in thread1=2*time in thread2=1time in thread2=2*time in thread1=3

20、time in thread1=4*time in thread2=3time in thread2=4*time in thread1=9time in thread1=10*time in thread2=9time in thread2=10*七、守護(hù)線程守護(hù)線程是一類特殊的線程,它和普通線程的區(qū)別在于它并不是應(yīng)用程序的核心部分,當(dāng)一個應(yīng)用程序的所有非守護(hù)線程終止運(yùn)行時,即使仍然有守護(hù)線程在運(yùn)行,應(yīng)用程序也將終止,反之,只要有一個非守護(hù)線程在運(yùn)行,應(yīng)用程序就不會終止。守護(hù)線程一般被用于在后臺為其它線程提供服務(wù)。調(diào)用方法isDaemon(來判斷一個線程是否是守護(hù)線程,也可以調(diào)用方法setD

21、aemon(將一個線程設(shè)為守護(hù)線程。八、一個應(yīng)用:線程池1.背景諸如Web 服務(wù)器、數(shù)據(jù)庫服務(wù)器、文件服務(wù)器或郵件服務(wù)器之類的許多服務(wù)器應(yīng)用程序都面向處理來自某些遠(yuǎn)程來源的大量短小的任務(wù)。服務(wù)器應(yīng)用程序中經(jīng)常出現(xiàn)的情況是:單個任務(wù)處理的時間很短而請求的數(shù)目卻是巨大的。2.解決方法構(gòu)建服務(wù)器應(yīng)用程序的一個過于簡單的模型應(yīng)該是:每當(dāng)一個請求到達(dá)就創(chuàng)建一個新線程,然后在新線程中為請求服務(wù)。那么這種方法的嚴(yán)重不足就很明顯。每個請求對應(yīng)一個線程(thread-per-request方法的不足之一是:為每個請求創(chuàng)建一個新線程的開銷很大;在一個JVM 里創(chuàng)建太多的線程可能會導(dǎo)致系統(tǒng)由于過度消耗內(nèi)存而用完內(nèi)存或“切換過度”。為了防止資源不足,服務(wù)器應(yīng)用程序需要一些辦法來限制任何給定時刻處理的請求數(shù)目。我們可以實現(xiàn)一個線程池類,其中客戶機(jī)類等待一個可用線程、將任務(wù)傳遞給該線程以便執(zhí)行、然后在任務(wù)完成時將線程歸還給池,一般一個簡單線程池至少包含下列組成部分:線程池管理器(ThreadPoolManager:用于創(chuàng)建并管理線程池工作線程(WorkThread:線程池中線程任務(wù)接口(T ask:每個任務(wù)必須實現(xiàn)的接口,以供工作線程調(diào)度任務(wù)的執(zhí)行。任務(wù)隊列:用于存放沒有處理的任務(wù)。提供一種緩沖機(jī)制。public clas

溫馨提示

  • 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)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論