Java執行緒的深入探討

azz發表於2007-08-23
Java執行緒的深入探討[@more@]  一般來說,我們把正在計算機中執行的程式叫做"程式"(Process) ,而不將其
  稱為程式(Program)。所謂"執行緒"(Thread),是"程式"中某個單一順序的控制流。
  新興的作業系統,如Mac,Windows NT,Windows 95等,大多采用多執行緒的概念,把線
  程視為基本執行單位。執行緒也是Java中的相當重要的組成部分之一。

  甚至最簡單的Applet也是由多個執行緒來完成的。在Java中,任何一個Applet的
  paint()和update()方法都是由AWT(Abstract Window Toolkit)繪圖與事件處理線
  程呼叫的,而Applet 主要的里程碑方法——init(),start(),stop()和destory()
  ——是由執行該Applet的應用呼叫的。

  單執行緒的概念沒有什麼新的地方,真正有趣的是在一個程式中同時使用多個線
  程來完成不同的任務。某些地方用輕量程式(Lightweig ht Process)來代替執行緒
  ,執行緒與真正程式的相似性在於它們都是單一順序控制流。然而執行緒被認為輕量是
  由於它執行於整個程式的上下文內,能使用整個程式共有的資源和程式環境。

  作為單一順序控制流,在執行的程式內執行緒必須擁有一些資源作為必要的開銷
  。例如,必須有執行堆疊和程式計數器。線上程內執行的程式碼只在它的上下文中起
  作用,因此某些地方用"執行上下文"來代替"執行緒"。

  2.執行緒屬性

  為了正確有效地使用執行緒,必須理解執行緒的各個方面並瞭解Java 實時系統。
  必須知道如何提供執行緒體、執行緒的生命週期、實時系統如 何排程執行緒、執行緒組、
  什麼是幽靈執行緒(Demo nThread)。

  (1)執行緒體
  所有的操作都發生線上程體中,在Java中執行緒體是從Thread類繼承的run()方
  法,或實現Runnable介面的類中的run()方法。當執行緒產生並初始化後,實時系統調
  用它的run()方法。run()方法內的程式碼實現所產生執行緒的行為,它是執行緒的主要部
  分。

  (2)執行緒狀態
  附圖表示了執行緒在它的生命週期內的任何時刻所能處的狀態以及引起狀態改
  變的方法。這圖並不是完整的有限狀態圖,但基本概括了執行緒中比較感興趣和普遍
  的方面。以下討論有關執行緒生命週期以此為據。


  ●新執行緒態(New Thread)
  產生一個Thread物件就生成一個新執行緒。當執行緒處於"新執行緒"狀態時,僅僅是
  一個空執行緒物件,它還沒有分配到系統資源。因此只能啟動或終止它。任何其他操
  作都會引發異常。
  ●可執行態(Runnable)
  start()方法產生執行執行緒所必須的資源,排程執行緒執行,並且呼叫執行緒的run
  ()方法。在這時執行緒處於可執行態。該狀態不稱為執行態是因為這時的執行緒並不
  總是一直佔用處理機。特別是對於只有一個處理機的PC而言,任何時刻只能有一個
  處於可執行態的執行緒佔用處理 機。Java透過排程來實現多執行緒對處理機的共享。

  ●非執行態(Not Runnable)
  當以下事件發生時,執行緒進入非執行態。
  ①suspend()方法被呼叫;
  ②sleep()方法被呼叫;
  ③執行緒使用wait()來等待條件變數;
  ④執行緒處於I/O等待。
  ●死亡態(Dead)
  當run()方法返回,或別的執行緒呼叫stop()方法,執行緒進入死亡態 。通常Appl
  et使用它的stop()方法來終止它產生的所有執行緒。

  (3)執行緒優先順序
  雖然我們說執行緒是併發執行的。然而事實常常並非如此。正如前面談到的,當
  系統中只有一個CPU時,以某種順序在單CPU情況下執行多執行緒被稱為排程(schedu
  ling)。Java採用的是一種簡單、固定的排程法,即固定優先順序排程。這種演算法是
  根據處於可執行態執行緒的相對優先順序來實行排程。當執行緒產生時,它繼承原執行緒的
  優先順序。在需要時可對優先順序進行修改。在任何時刻,如果有多條執行緒等待執行,
  系統選擇優先順序最高的可執行執行緒執行。只有當它停止、自動放棄、或由於某種
  原因成為非執行態低優先順序的執行緒才能執行。如果兩個執行緒具有相同的優先順序,它
  們將被交替地執行。
  Java實時系統的執行緒排程演算法還是強制性的,在任何時刻,如果一個比其他線
  程優先順序都高的執行緒的狀態變為可執行態,實時系統將選擇該執行緒來執行。

  (4)幽靈執行緒
  任何一個Java執行緒都能成為幽靈執行緒。它是作為執行於同一個程式內的物件
  和執行緒的服務提供者。例如,HotJava瀏覽器有一個稱為" 後臺圖片閱讀器"的幽靈
  執行緒,它為需要圖片的物件和執行緒從檔案系統或網路讀入圖片。
  幽靈執行緒是應用中典型的獨立執行緒。它為同一應用中的其他物件和執行緒提供
  服務。幽靈執行緒的run()方法一般都是無限迴圈,等待服務請求。

  (5)執行緒組
  每個Java執行緒都是某個執行緒組的成員。執行緒組提供一種機制,使得多個執行緒集
  於一個物件內,能對它們實行整體操作。譬如,你能用一個方法呼叫來啟動或掛起
  組內的所有執行緒。Java執行緒組由ThreadGroup類實現。
  當執行緒產生時,可以指定執行緒組或由實時系統將其放入某個預設的執行緒組內。
  執行緒只能屬於一個執行緒組,並且當執行緒產生後不能改變它所屬的執行緒組。

  3.多執行緒程式

  對於多執行緒的好處這就不多說了。但是,它同樣也帶來了某些新的麻煩。只要
  在設計程式時特別小心留意,克服這些麻煩並不算太困難。

  (1)同步執行緒
  許多執行緒在執行中必須考慮與其他執行緒之間共享資料或協調執行狀態。這就
  需要同步機制。在Java中每個物件都有一把鎖與之對應。但Java不提供單獨的lo
  ck和unlock操作。它由高層的結構隱式實現, 來保證操作的對應。(然而,我們注
  意到Java虛擬機器提供單獨的monito renter和monitorexit指令來實現lock和unlo
  ck操作。)
  synchronized語句計算一個物件引用,試圖對該物件完成鎖操作, 並且在完成
  鎖操作前停止處理。當鎖操作完成synchronized語句體得到執行。當語句體執行
  完畢(無論正常或異常),解鎖操作自動完成。作為物件導向的語言,synchronized
  經常與方法連用。一種比較好的辦法是,如果某個變數由一個執行緒賦值並由別的線
  程引用或賦值,那麼所有對該變數的訪問都必須在某個synchromized語句或synch
  ronized方法內。
  現在假設一種情況:執行緒1與執行緒2都要訪問某個資料區,並且要求執行緒1的訪
  問先於執行緒2, 則這時僅用synchronized是不能解決問題的。這在Unix或Windows
  NT中可用Simaphore來實現。而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為假,則呼叫wait()。wait()的作用是使線
  程2進入非執行態,並且解鎖。在這種情況下,method-1可以被執行緒1呼叫。當執行
  notify()後。執行緒2由非執行態轉變為可執行態。當method-1呼叫返回後。執行緒2
  可重新對該物件加鎖,加鎖成功後執行wait()返回後的指令。這種機制也能適用於
  其他更復雜的情況。

  (2)死鎖
  如果程式中有幾個競爭資源的併發執行緒,那麼保證均衡是很重要的。系統均衡
  是指每個執行緒在執行過程中都能充分訪問有限的資源。系統中沒有餓死和死鎖的
  執行緒。Java並不提供對死鎖的檢測機制。對大多數的Java程式設計師來說防止死鎖是
  一種較好的選擇。最簡單的防止死鎖的方法是對競爭的資源引入序號,如果一個線
  程需要幾個資源,那麼它必須先得到小序號的資源,再申請大序號的資源。

  4.小結

  執行緒是Java中的重要內容,多執行緒是Java的一個特點。雖然Java的同步互斥不
  如某些系統那麼豐富,但適當地使用它們也能收到滿意的效果。

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

相關文章