Java線程的深入探討
發(fā)表時間:2024-01-17 來源:明輝站整理相關軟件相關文章人氣:
[摘要]一般來說,我們把正在計算機中執(zhí)行的程序叫做"進程"(Process) ,而不將其 稱為程序(Program)。所謂"線程"(Thread),是"進程"中某個單一順序的控制流。 新興的操作系統(tǒng),如Mac,Windows NT,Windows...
一般來說,我們把正在計算機中執(zhí)行的程序叫做"進程"(Process) ,而不將其
稱為程序(Program)。所謂"線程"(Thread),是"進程"中某個單一順序的控制流。
新興的操作系統(tǒng),如Mac,Windows NT,Windows 95等,大多采用多線程的概念,把線
程視為基本執(zhí)行單位。線程也是Java中的相當重要的組成部分之一。
甚至最簡單的Applet也是由多個線程來完成的。在Java中,任何一個Applet的
paint()和update()方法都是由AWT(Abstract Window Toolkit)繪圖與事件處理線
程調(diào)用的,而Applet 主要的里程碑方法——init(),start(),stop()和destory()
——是由執(zhí)行該Applet的應用調(diào)用的。
單線程的概念沒有什么新的地方,真正有趣的是在一個程序中同時使用多個線
程來完成不同的任務。某些地方用輕量進程(Lightweig ht Process)來代替線程
,線程與真正進程的相似性在于它們都是單一順序控制流。然而線程被認為輕量是
由于它運行于整個程序的上下文內(nèi),能使用整個程序共有的資源和程序環(huán)境。
作為單一順序控制流,在運行的程序內(nèi)線程必須擁有一些資源作為必要的開銷
。例如,必須有執(zhí)行堆棧和程序計數(shù)器。在線程內(nèi)執(zhí)行的代碼只在它的上下文中起
作用,因此某些地方用"執(zhí)行上下文"來代替"線程"。
2.線程屬性
為了正確有效地使用線程,必須理解線程的各個方面并了解Java 實時系統(tǒng)。
必須知道如何提供線程體、線程的生命周期、實時系統(tǒng)如 何調(diào)度線程、線程組、
什么是幽靈線程(Demo nThread)。
(1)線程體
所有的操作都發(fā)生在線程體中,在Java中線程體是從Thread類繼承的run()方
法,或?qū)崿F(xiàn)Runnable接口的類中的run()方法。當線程產(chǎn)生并初始化后,實時系統(tǒng)調(diào)
用它的run()方法。run()方法內(nèi)的代碼實現(xiàn)所產(chǎn)生線程的行為,它是線程的主要部
分。
(2)線程狀態(tài)
附圖表示了線程在它的生命周期內(nèi)的任何時刻所能處的狀態(tài)以及引起狀態(tài)改
變的方法。這圖并不是完整的有限狀態(tài)圖,但基本概括了線程中比較感興趣和普遍
的方面。以下討論有關線程生命周期以此為據(jù)。
●新線程態(tài)(New Thread)
產(chǎn)生一個Thread對象就生成一個新線程。當線程處于"新線程"狀態(tài)時,僅僅是
一個空線程對象,它還沒有分配到系統(tǒng)資源。因此只能啟動或終止它。任何其他操
作都會引發(fā)異常。
●可運行態(tài)(Runnable)
start()方法產(chǎn)生運行線程所必須的資源,調(diào)度線程執(zhí)行,并且調(diào)用線程的run
()方法。在這時線程處于可運行態(tài)。該狀態(tài)不稱為運行態(tài)是因為這時的線程并不
總是一直占用處理機。特別是對于只有一個處理機的PC而言,任何時刻只能有一個
處于可運行態(tài)的線程占用處理 機。Java通過調(diào)度來實現(xiàn)多線程對處理機的共享。
●非運行態(tài)(Not Runnable)
當以下事件發(fā)生時,線程進入非運行態(tài)。
①suspend()方法被調(diào)用;
、趕leep()方法被調(diào)用;
③線程使用wait()來等待條件變量;
、芫程處于I/O等待。
●死亡態(tài)(Dead)
當run()方法返回,或別的線程調(diào)用stop()方法,線程進入死亡態(tài) 。通常Appl
et使用它的stop()方法來終止它產(chǎn)生的所有線程。
(3)線程優(yōu)先級
雖然我們說線程是并發(fā)運行的。然而事實常常并非如此。正如前面談到的,當
系統(tǒng)中只有一個CPU時,以某種順序在單CPU情況下執(zhí)行多線程被稱為調(diào)度(schedu
ling)。Java采用的是一種簡單、固定的調(diào)度法,即固定優(yōu)先級調(diào)度。這種算法是
根據(jù)處于可運行態(tài)線程的相對優(yōu)先級來實行調(diào)度。當線程產(chǎn)生時,它繼承原線程的
優(yōu)先級。在需要時可對優(yōu)先級進行修改。在任何時刻,如果有多條線程等待運行,
系統(tǒng)選擇優(yōu)先級最高的可運行線程運行。只有當它停止、自動放棄、或由于某種
原因成為非運行態(tài)低優(yōu)先級的線程才能運行。如果兩個線程具有相同的優(yōu)先級,它
們將被交替地運行。
Java實時系統(tǒng)的線程調(diào)度算法還是強制性的,在任何時刻,如果一個比其他線
程優(yōu)先級都高的線程的狀態(tài)變?yōu)榭蛇\行態(tài),實時系統(tǒng)將選擇該線程來運行。
(4)幽靈線程
任何一個Java線程都能成為幽靈線程。它是作為運行于同一個進程內(nèi)的對象
和線程的服務提供者。例如,HotJava瀏覽器有一個稱為" 后臺圖片閱讀器"的幽靈
線程,它為需要圖片的對象和線程從文件系統(tǒng)或網(wǎng)絡讀入圖片。
幽靈線程是應用中典型的獨立線程。它為同一應用中的其他對象和線程提供
服務。幽靈線程的run()方法一般都是無限循環(huán),等待服務請求。
(5)線程組
每個Java線程都是某個線程組的成員。線程組提供一種機制,使得多個線程集
于一個對象內(nèi),能對它們實行整體操作。譬如,你能用一個方法調(diào)用來啟動或掛起
組內(nèi)的所有線程。Java線程組由ThreadGroup類實現(xiàn)。
當線程產(chǎn)生時,可以指定線程組或由實時系統(tǒng)將其放入某個缺省的線程組內(nèi)。
線程只能屬于一個線程組,并且當線程產(chǎn)生后不能改變它所屬的線程組。
3.多線程程序
對于多線程的好處這就不多說了。但是,它同樣也帶來了某些新的麻煩。只要
在設計程序時特別小心留意,克服這些麻煩并不算太困難。
(1)同步線程
許多線程在執(zhí)行中必須考慮與其他線程之間共享數(shù)據(jù)或協(xié)調(diào)執(zhí)行狀態(tài)。這就
需要同步機制。在Java中每個對象都有一把鎖與之對應。但Java不提供單獨的lo
ck和unlock操作。它由高層的結(jié)構(gòu)隱式實現(xiàn), 來保證操作的對應。(然而,我們注
意到Java虛擬機提供單獨的monito renter和monitorexit指令來實現(xiàn)lock和unlo
ck操作。)
synchronized語句計算一個對象引用,試圖對該對象完成鎖操作, 并且在完成
鎖操作前停止處理。當鎖操作完成synchronized語句體得到執(zhí)行。當語句體執(zhí)行
完畢(無論正;虍惓),解鎖操作自動完成。作為面向?qū)ο蟮恼Z言,synchronized
經(jīng)常與方法連用。一種比較好的辦法是,如果某個變量由一個線程賦值并由別的線
程引用或賦值,那么所有對該變量的訪問都必須在某個synchromized語句或synch
ronized方法內(nèi)。
現(xiàn)在假設一種情況:線程1與線程2都要訪問某個數(shù)據(jù)區(qū),并且要求線程1的訪
問先于線程2, 則這時僅用synchronized是不能解決問題的。這在Unix或Windows
NT中可用Simaphore來實現(xiàn)。而Java并不提供。在Java中提供的是wait()和noti
fy()機制。使用如下:
synchronized method-1(…){ call by thread 1.
∥access data area;
available=true;
notify()
}
synchronized method-2(…){∥call by thread 2.
while(!available)
try{
wait();∥wait for notify().
}catch (Interrupted Exception e){
}
∥access data area
}
其中available是類成員變量,置初值為false。
如果在method-2中檢查available為假,則調(diào)用wait()。wait()的作用是使線
程2進入非運行態(tài),并且解鎖。在這種情況下,method-1可以被線程1調(diào)用。當執(zhí)行
notify()后。線程2由非運行態(tài)轉(zhuǎn)變?yōu)榭蛇\行態(tài)。當method-1調(diào)用返回后。線程2
可重新對該對象加鎖,加鎖成功后執(zhí)行wait()返回后的指令。這種機制也能適用于
其他更復雜的情況。
(2)死鎖
如果程序中有幾個競爭資源的并發(fā)線程,那么保證均衡是很重要的。系統(tǒng)均衡
是指每個線程在執(zhí)行過程中都能充分訪問有限的資源。系統(tǒng)中沒有餓死和死鎖的
線程。Java并不提供對死鎖的檢測機制。對大多數(shù)的Java程序員來說防止死鎖是
一種較好的選擇。最簡單的防止死鎖的方法是對競爭的資源引入序號,如果一個線
程需要幾個資源,那么它必須先得到小序號的資源,再申請大序號的資源。
4.小結(jié)
線程是Java中的重要內(nèi)容,多線程是Java的一個特點。雖然Java的同步互斥不
如某些系統(tǒng)那么豐富,但適當?shù)厥褂盟鼈円材苁盏綕M意的效果。