Java執行緒的討論與應用(轉)

ba發表於2007-08-15
Java執行緒的討論與應用(轉)[@more@]一、為什麼要研究和使用執行緒
一般來說,計算機正在執行的程式稱作程式(process),程式有不同的地址空間並且是在同一系統上執行的不同程式,如WORD和Excel,程式間的通訊是很費時而且有限的。上下文切換、改變執行的程式也是非常複雜的。程式間通訊複雜,可能需要管道、訊息佇列、共享記憶體 (sharedmemory)或訊號處理來保證程式間的通訊。儘管許多程式都在執行,但一次只能與一個程式打交道。
執行緒(thread)是指程式中單一順序的控制流。又稱為輕量級程式。執行緒則共享相同的地址空間並共同構成一個大的程式。執行緒間的通訊是非常簡單而有效的,上下文切換非常快並且是整個大程式的一部分切換。執行緒僅是過程呼叫,它們彼此獨立執行,執行緒使得在一個應用程式中,程式的編寫更加自由和豐富。執行緒的興趣在於,一個程式中同時使用多個執行緒來完成不同的任務。因此如果很好地利用執行緒,可以大大簡化應用程式設計。多執行緒可以增程式序的互動性,提供更好的能力和功能、更好的GUI和更好的伺服器功能。給二個例子說明如下:

例一:利用多執行緒並行機制可以很好地解決互動式網路程式中的許多問題,如:大量的網路檔案資源的讀寫、使用者輸入響應、動畫顯示等問題不需要CPU的多少時間;而耗時的複雜計算通常並不需要立即響應,所以無需將CPU全給它。例如,從一個慢速的網路上讀取一資料流也許要1分鐘時間,但需要CPU參與傳輸資料的時間則非常短;響應使用者的輸入如擊鍵,就算最快的輸入員,1秒鐘擊鍵10次,也不需要CPU的多少時間。動畫程式比較耗時,一幅畫在1秒內要重繪5-10次,但CPU在大部分時間仍處於空閒狀態。在傳統的單執行緒環境下的問題是使用者必須等待每個任務完成後才能進行下一個任務。即使CPU大部分時間空閒,也只能按步就班地工作。多執行緒可以很好地解決這些問題避免引起使用者的等待。如:耗時的複雜計算應用就可劃分成兩個控制執行緒:一個處理GUI的使用者事件,另一個進行後臺計算。

例二:如併發伺服器,它面向不定長時間內處理完的請求,對每個請求由伺服器的執行緒處理。傳統的併發伺服器往往是基於多程式機制的,每個客戶一個程式,需要作業系統的干預,程式的數目受作業系統的限制。本文利用Java的執行緒機制建立了基於多執行緒的併發伺服器。生成和管理他們是相當簡單的操作。執行緒被用來建立請求驅動的服務程式,每個客戶一個執行緒,多個執行緒可以併發執行。特別地執行緒具有如下特性(1)執行緒共享父程式的所有程式和資料(2)有自身的執行單元(3)有它自己的私有儲存和執行環境(尤其是處理器暫存器),使得伺服器程式不隨客戶數的增加而線性增加。可減少伺服器程式的壓力,降低開銷,充分利用CPU的資源。以上併發伺服器在某一瞬間由同一伺服器程式所產生的多個併發執行緒對多個客戶的併發請求採取分而治之的措施,從而解決了併發請求的問題。各執行緒即可以獨立操作,又可以協同作業。降低了伺服器的複雜度。

Java是基於作業系統級的多執行緒環境之上設計的,Java的執行器依靠多執行緒來執行任務,並且所有類庫在設計時都考慮到多執行緒機制。

二、Java執行緒的結構
Java支援一種“搶佔式”(preemptive)排程方式。
執行緒從產生到消失,可分5個狀態:
Newborn
執行緒在己被建立但未執行這段時間內,處於一個特殊的"Newborn"狀態,這時,執行緒物件己被分配記憶體空間,其私有資料己被初始化,但該執行緒還未被排程。此時執行緒物件可透過start()方法排程,或者利用stop()方法殺死.新建立的執行緒一旦被排程,就將切換到"Runnable"狀態。

Runnable
Runnable意即執行緒的就緒狀態,表示執行緒正等待處理器資源,隨時可被呼叫執行。處於就緒狀態的執行緒事實上己被排程,也就是說,它們己經被放到某一佇列等待執行。處於就緒狀態的執行緒何時可真正執行,取決於執行緒優先順序以及佇列的當前狀況。執行緒的優先順序如果相同,將遵循"先來先服務"的排程原則。

執行緒依據自身優先順序進入等待佇列的相應位置。某些系統執行緒具有最高優先順序,這些最高優先順序執行緒一旦進入就緒狀態,將搶佔當前正在執行的執行緒的處理器資源,當前執行緒只能重新在等待佇列尋找自己的位置.這些具有最高優先順序的執行緒執行完自己的任務之後,將睡眠一段時間,等待被某一事件喚醒.一旦被喚,這些執行緒就又開始搶佔處理器資源。這些最高優先順序執行緒通常用來執行一些關鍵性任務,如螢幕顯示。

低優先順序執行緒需等待更長的時間才能有機會執行。由於系統本身無法中止高優先順序執行緒的執行,因此,如果你的程式中用到了優先順序較高的執行緒物件,那麼最好不時讓這些執行緒放棄對處理器資源的控制權,以使其他執行緒能夠有機執行。

Running
"Running"(執行)狀態表明執行緒正在執行,該線己經擁有了對處理器的控制權,其程式碼目前正在執行。這個執行緒將一直執行直到執行完畢,除非執行過程的控制權被一優先順序更高的執行緒強佔。

綜合起來,執行緒在如下3種情形之下將釋放對處理器的控制權:

1.主動或被動地釋放對處理器資源的控制權。這時,該執行緒必須再次進入等待佇列,等待其他優先順序高或相等執行緒執行完畢。

2.睡眠一段確定的時間,不進入等待佇列。這段確定的時間段到期之後,重新開始執行。

3.等待某一事件喚醒自己。

Blocked
一個執行緒如果處於"Blocked"(堵塞)狀態,那麼暫時這個執行緒將無法進入就緒佇列。處於堵塞狀態的執行緒通常必須由某些事件才能喚醒。至於是何種事件,則取決於堵塞發生的原因:處於睡眠中的執行緒必須被堵塞一段固定的時間;被掛起、或處於訊息等待狀態的執行緒則必須由一外來事件喚醒。

Dead
Dead表示執行緒巳退出執行狀態,並且不再進入就緒佇列.其中原因可能是執行緒巳執行完畢(正常結束),也可能是該執行緒被另一執行緒所強行中斷(kill)。

三、建立和使用執行緒的基本方法
1.執行緒的產生
在Java語言中,可採用兩種方式產生執行緒:一是實現一個Runnable介面,二是擴充一個Thread類.java.lang中定義了一個直接從根類Object中派生的Thread類.所有以這個類派生的子類或間接子類,均為執行緒。在這種方式中,需要作為一個執行緒執行的類只能繼承、擴充單一的父類。下面的例子透過擴充Thread類,用該執行緒自己的實現來覆蓋Thread.run(),產生一個新類Counter。run()方法是 Counter類執行緒所作的全部操作.
import java.lang.*; public class Counter extends Thread { public void run () {....} }

實現Runnable介面是最常用的產生執行緒的方法,它打破了擴充Thread類方式的限制。
Java語言原始碼中,Runnable介面只包含了一個抽象方法,其定義如下:
package java.lang.*; public interface Runnable { public abstract void run (); }

所有實現了Runnable介面的類的物件都可以以執行緒方式執行.下面的例子產生與上面例子相同的類.可以看到counter類中使用了一個Thread類的變數.
import java.lang.*;
public class counter implements Runnable { Thread T; public void run () {...} }
2、基本方法
.public synchronized void start()

啟動執行緒物件,呼叫其run()方法,隨即返回。

.pubilc final void stop()

停止執行緒的執行。

.public final void resume()

喚醒被掛起的執行緒。只在呼叫suspend()之後有效。

.public final void suspend()

掛起執行緒的執行。

.public static void yield()

暫時中止當前正在執行的執行緒物件的執行。若存在其他執行緒,則隨後呼叫下一個執行緒。

.public static void sleep(longmills)throws Inter rupted Exception

使當前正處執行狀態的執行緒睡眠mills毫秒。

.public final void wait()throws Interrupted Exception

使執行緒進入等待狀態,直到被另一執行緒喚醒

.public final void motify()

把執行緒狀態的變化通知給另一等待執行緒。

四、執行緒的同步
執行緒的使用,主要在於一個程式中多個執行緒的協同工作,所以執行緒的同步就很重要。執行緒的同步用於執行緒共享資料,轉換和控制執行緒的執行,保證記憶體的一致性。
在Java中,執行環境使用程式(Monitor)來解決執行緒同步的問題。管程是一種併發同步機制,它包括用於分配一個特定的共享資源或一組共享資源的資料和方法.

Java為每一個擁有synchronized方法的物件例項提供了一個唯一的管程。為了完成分配資源的功能,執行緒必須呼叫管程入口。管程入口就是synchronized方法入口。當呼叫同步(synchronized)方法時,該執行緒就獲得了該管程。

管程邊界上實行嚴格的互斥,在同一時刻,只允許一個執行緒進入管程;當管程中已有了一個執行緒時,其它希望進入管程的執行緒必須等待,這種等待是由管程自動管理的。

如果呼叫管程入口的執行緒發現資源已被分配,管程中的這個執行緒將呼叫等待操作wait()。進入wait()後,該執行緒放棄佔用管程,在管程外面等待,以便其它執行緒進入管程。

最終,佔用資源的執行緒將呼叫一個管程的入口把資源歸還給系統,此時,該執行緒需呼叫一個通知操作notify(),通知系統允許其中一個等待的執行緒獲得管程並得到資源。被通知的執行緒是排隊的,從而避免無限拖延。

在Java.lang中提供了用來編寫管程的兩個方法:notify()和wait()。此外還有notifyAll(),它通知所有等待的執行緒,使它們競爭管程,結果是其中一個獲得管程,其佘返回等待狀態。

五、執行緒的控制
執行緒的控制分為停止執行緒和啟動執行緒。
.publicfinalvoidsuspend()

掛起執行緒的執行。

.publicfinalvoidresume()

喚醒被掛起的執行緒。使一個暫停的執行緒可用於排程。

因為執行緒的排程為搶佔式機制,也可使用執行緒的優先順序來對執行緒進行控制。

.publicfinalvoidsetPriority(intnewPriority)

設定執行緒優先順序。

.publicfinalintgetPriority()

獲取並返回執行緒的優先順序。

執行緒的優先順序用於在執行佇列中給執行緒排序,Java提供的搶佔式排程,使得高階別的執行緒先執行。

六、執行緒的應用
在實際應用中,執行緒使用的範圍很廣,可用於控制實時資料處理、快速的網路服務,還有更快的圖象繪製和列印,以及資料庫中的資料的取回和處理等等。在Java中一個在不停執行的提供一些基本服務的例子是垃圾收集執行緒,垃圾收集執行緒,。該執行緒由Java虛擬機器提供。它掃描程式中不再被訪問的變數,將其所佔的系統資源釋放給系統。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10617731/viewspace-958494/,如需轉載,請註明出處,否則將追究法律責任。

相關文章