標籤:
Java運行系統在很多方面依賴於線程,所有的類庫設計都考慮到多線程。實際上,Java使用線程來使整個環境非同步。這有利於通過防止CPU迴圈的浪費來減少無效部分。
為更好的理解多線程環境的優勢可以將它與它的對照物相比較。單線程系統的處理途徑是使用一種叫作輪詢的事件迴圈方法。在該模型中,單線程式控制制在一無限迴圈中運行,輪詢一個事件序列來決定下一步做什麼。一旦輪詢裝置返回訊號表明,已準備好讀取網路檔案,事件迴圈調度控制管理到適當的事件處理常式。直到事件處理常式返回,系統中沒有其他事件發生。這就浪費了CPU時間。這導致了程式的一部分獨佔了系統,阻止了其他事件的執行。總的來說,單線程環境,當一個線程因為等待資源時阻塞(block,掛起執行),整個程式停止運行。
Java多線程的優點在於取消了主迴圈/輪詢機制。一個線程可以暫停而不影響程式的其他部分。例如,當一個線程從網路讀取資料或等待使用者輸入時產生的空閑時間可以被利用到其他地方。多線程允許活的迴圈在每一幀間隙中沉睡一秒而不暫停整個系統。在Java程式中出現線程阻塞,僅有一個線程暫停,其他線程繼續運行。
線程存在於好幾種狀態。線程可以正在運行(running)。只要獲得CPU時間它就可以運行。啟動並執行線程可以被掛起(suspend),並臨時中斷它的執行。一個掛起的線程可以被恢複(resume,允許它從停止的地方繼續運行。一個線程可以在等待資源時被阻塞(block)。
在任何時候,線程可以終止(terminate),這立即中斷了它的運行。一旦終止,線程不能被恢複。
線程優先順序
Java給每個線程安排優先順序以決定與其他線程比較時該如何對待該線程。線程優先順序是詳細說明線程間優先關係的整數。作為絕對值,優先順序是毫無意義的;當只有一個線程時,優先順序高的線程並不比優先權低的線程啟動並執行快。相反,線程的優先順序是用來決定何時從一個啟動並執行線程切換到另一個。這叫“上下文轉換”(context switch)。決定上下文轉換髮生的規則很簡單:
- 線程可以自動放棄控制。在I/O未決定的情況下,睡眠或阻塞由明確的讓步來完成。在這種假定下,所有其他的線程被檢測,準備啟動並執行最高優先順序線程被授予CPU。
- 線程可以被高優先順序的線程搶佔。在這種情況下,低優先順序線程不主動放棄,處理器只是被先佔——無論它正在幹什麼——處理器被高優先順序的線程佔據。基本上,一旦高優先順序線程要運行,它就執行。這叫做有優先權的多任務處理。
當兩個相同優先順序的線程競爭CPU周期時,情形有一點複雜。對於Windows98這樣的作業系統,等優先順序的線程是在迴圈模式下自動劃分時間的。對於其他動作系統,例如Solaris 2.x,等優先順序線程相對於它們的對等體自動放棄。如果不這樣,其他的線程就不會運行。
警告:不同的作業系統下等優先順序線程的上下文轉換可能會產生錯誤。
同步性
因為多線程在你的程式中引入了一個非同步行為,所以在你需要的時候必須有加強同步性的方法。舉例來說,如果你希望兩個線程相互連信並共用一個複雜的資料結構,例如鏈表序列,你需要某些方法來確保它們沒有相互衝突。也就是說,你必須防止一個線程寫入資料而另一個線程正在讀取鏈表中的資料。為此目的,Java在進程間同步性的老模式基礎上實行了另一種方法:管程(monitor)。管程是一種由C.A.R.Hoare首先定義的控制機制。
你可以把管程想象成一個僅控制一個線程的小盒子。一旦線程進入管程,所有線程必須等待直到該線程退出了管程。用這種方法,管程可以用來防止共用的資源被多個線程操縱。
很多多線程系統把管程作為程式必須明確的引用和操作的對象。Java提供一個清晰的解決方案。沒有“Monitor”類;相反,每個對象都擁有自己的隱式管程,當對象的同步方法被調用時管程自動載入。一旦一個線程包含在一個同步方法中,沒有其他線程可以調用相同對象的同步方法。這就使你可以編寫非常清晰和簡潔的多線程代碼,因為同步支援是語言內建的。
訊息傳遞
在你把程式分成若干線程後,你就要定義各線程之間的聯絡。用大多數其他語言規劃時,你必須依賴於作業系統來確立線程間通訊。這樣當然增加花費。然而,Java提供了多線程間談話清潔的、低成本的途徑——通過調用所有對象都有的預先確定的方法。Java的訊息傳遞系統允許一個線程進入一個對象的一個同步方法,然後在那裡等待,直到其他線程明確通知它出來。
Thread 類和Runnable 介面
Java的多線程系統建立於Thread類,它的方法,它的共伴介面Runnable基礎上。Thread類封裝了線程的執行。既然你不能直接引用運行著的線程的狀態,你要通過它的代理處理它,於是Thread 執行個體產生了。為建立一個新的線程,你的程式必須擴充Thread 或實現Runnable介面。
Thread類定義了好幾種方法來協助管理線程。本章用到的方法如表11-1所示:
表 11-1 管理線程的方法
| 方法 |
意義 |
| getName |
獲得線程名稱 |
| getPriority |
獲得線程優先順序 |
| jsAlive |
判定線程是否仍在運行 |
| join |
等待一個線程終止 |
| run |
線程的進入點. |
| sleep |
在一段時間內掛起線程 |
| start |
通過調用運行方法來啟動線程 |
到目前為止,本書所應用的例子都是用單線程的。本章剩餘部分解釋如何用Thread 和 Runnable 來建立、管理線程。讓我們從所有Java程式都有的線程:主線程開始。
系列文章:
Java知多少(上)
Java知多少(39)interface介面
Java知多少(40)介面和抽象類別的區別
Java知多少(41)泛型詳解
Java知多少(42)泛型萬用字元和型別參數的範圍
Java知多少(43)異常處理基礎
Java知多少(44)異常類型
Java知多少(45)未被捕獲的異常
Java知多少(46)try和catch的使用
Java知多少(47)多重catch語句的使用
Java知多少(48)try語句的嵌套
Java知多少(49)throw:異常的拋出
Java知多少(50)Java throws子句
Java知多少(51)finally
Java知多少(52)內建異常
Java知多少(53)使用Java建立自己的異常子類
Java知多少(54)斷言詳解
Java知多少(55)線程
Java知多少(56)執行緒模式