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