程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java線程的討論與應用

Java線程的討論與應用

編輯:關於JAVA

一、為什麼要研究和使用線程

一般來說,計算機正在執行的程序稱作進程(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類線程所作的全部操作.

importjava.lang.*;
publicclassCounterextendsThread
{
publicvoidrun()
{....}
}

實現Runnable界面是最常用的產生線程的方法,它打破了擴充Thread類方式的限制。

Java語言源碼中,Runnable界面只包含了一個抽象方法,其定義如下:

packagejava.lang.*;
publicinterfaceRunnable{
publicabstractvoidrun();
}

所有實現了Runnable界面的類的對象都可以以線程方式執行.下面的例子產生與上面例子相同的類.可以看到counter類中使用了一個Thread類的變量.

importjava.lang.*;
publicclasscounterimplementsRunnable
{ThreadT;
publicvoidrun()
{...}
}

2、基本方法

.publicsynchronizedvoidstart()

啟動線程對象,調用其run()方法,隨即返回。

.pubilcfinalvoidstop()

停止線程的執行。

.publicfinalvoidresume()

喚醒被掛起的線程。只在調用suspend()之後有效。

.publicfinalvoidsuspend()

掛起線程的執行。

.publicstaticvoidyield()

暫時中止當前正在執行的線程對象的運行。若存在其他線程,則隨後調用下一個線程。

.publicstaticvoidsleep(longmills)throwsInterruptedException

使當前正處運行狀態的線程睡眠mills毫秒。

.publicfinalvoidwait()throwsInterruptedException

使線程進入等待狀態,直到被另一線程喚醒

.publicfinalvoidmotify()

把線程狀態的變化通知給另一等待線程。

四、線程的同步

線程的使用,主要在於一個進程中多個線程的協同工作,所以線程的同步就很重要。線程的同步用於線程共享數據,轉換和控制線程的執行,保證內存的一致性。

在Java中,運行環境使用程序(Monitor)來解決線程同步的問題。管程是一種並發同步機制,它包括用於分配一個特定的共享資源或一組共享資源的數據和方法.

Java為每一個擁有synchronized方法的對象實例提供了一個唯一的管程。為了完成分配資源的功能,線程必須調用管程入口。管程入口就是synchronized方法入口。當調用同步(synchronized)方法時,該線程就獲得了該管程。

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

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

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

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

五、線程的控制

線程的控制分為停止線程和啟動線程。

.publicfinalvoidsuspend()

掛起線程的執行。

.publicfinalvoidresume()

喚醒被掛起的線程。使一個暫停的線程可用於調度。

因為線程的調度為搶占式機制,也可使用線程的優先級來對線程進行控制。

.publicfinalvoidsetPriority(intnewPriority)

設置線程優先級。

.publicfinalintgetPriority()

獲取並返回線程的優先級。

線程的優先級用於在運行隊列中給線程排序,Java提供的搶占式調度,使得高級別的線程先運行。

六、線程的應用

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved