| 導購 | 订阅 | 在线投稿
分享
 
 
 

編寫多線程的Java應用程序-如何避免當前編程中最常見的問題

來源:互聯網  2008-05-31 12:10:50  評論

幾乎所有使用 AWT 或 Swing 編寫的畫圖程序都需要多線程。但多線程程序會造成許多困難,剛開始編程的開發者經常會發現他們被一些問題所折磨,例如不正確的程序行爲或死鎖。

在本文中,我們將探討使用多線程時碰到的問題,並提出那些常見陷阱的解決方案。

線程是什麽?

一個程序或進程能夠包含多個線程,這些線程可以根據程序的代碼執行相應的指令。多線程看上去似乎在並行執行它們各自的工作,就像在一台計算機上運行著多個處理機一樣。在多處理機計算機上實現多線程時,它們確實可以並行工作。和進程不同的是,線程共享地址空間。也就是說,多個線程能夠讀寫相同的變量或數據結構。

編寫多線程程序時,你必須注重每個線程是否幹擾了其他線程的工作。可以將程序看作一個辦公室,假如不需要共享辦公室資源或與其他人交流,所有職員就會獨立並行地工作。某個職員若要和其他人交談,當且僅當該職員在「聽」且他們兩說同樣的語言。此外,只有在複印機空閑且處于可用狀態(沒有僅完成一半的複印工作,沒有紙張阻塞等問題)時,職員才能夠使用它。在這篇文章中你將看到,在 Java 程序中互相協作的線程就似乎是在一個組織良好的機構中工作的職員。

在多線程程序中,線程可以從預備就緒隊列中得到,並在可獲得的系統 CPU 上運行。操作系統可以將線程從處理器移到預備就緒隊列或阻塞隊列中,這種情況可以認爲是處理器「挂起」了該線程。同樣,Java 虛擬機 (JVM) 也可以控制線程的移動??在協作或搶先模型中??從預備就緒隊列中將進程移到處理器中,于是該線程就可以開始執行它的程序代碼。

協作式線程模型答應線程自己決定什麽時候放棄處理器來等待其他的線程。程序開發員可以精確地決定某個線程何時會被其他線程挂起,答應它們與對方有效地合作。缺點在于某些惡意或是寫得不好的線程會消耗所有可獲得的 CPU 時間,導致其他線程「饑餓」。

在搶占式線程模型中,操作系統可以在任何時候打斷線程。通常會在它運行了一段時間(就是所謂的一個時間片)後才打斷它。這樣的結果自然是沒有線程能夠不公平地長時間霸占處理器。然而,隨時可能打斷線程就會給程序開發員帶來其他麻煩。同樣使用辦公室的例子,假設某個職員搶在另一人前使用複印機,但打印工作在未完成的時候離開了,另一人接著使用複印機時,該複印機上可能就還有先前那名職員留下來的資料。搶占式線程模型要求線程正確共享資源,協作式模型卻要求線程共享執行時間。由于 JVM 規範並沒有非凡規定線程模型,Java 開發員必須編寫可在兩種模型上正確運行的程序。在了解線程以及線程間通訊的一些方面之後,我們可以看到如何爲這兩種模型設計程序。

線程和 Java 語言

爲了使用 Java 語言創建線程,你可以生成一個 Thread 類(或其子類)的對象,並給這個對象發送 start() 消息。(程序可以向任何一個派生自 Runnable 接口的類對象發送 start() 消息。)每個線程動作的定義包含在該線程對象的 run() 方法中。run 方法就相當于傳統程序中的 main() 方法;線程會持續運行,直到 run() 返回爲止,此時該線程便死了。

上鎖

大多數應用程序要求線程互相通信來同步它們的動作。在 Java 程序中最簡單實現同步的方法就是上鎖。爲了防止同時訪問共享資源,線程在使用資源的前後可以給該資源上鎖和開鎖。假想給複印機上鎖,任一時刻只有一個職員擁有鑰匙。若沒有鑰匙就不能使用複印機。給共享變量上鎖就使得 Java 線程能夠快速方便地通信和同步。某個線程若給一個對象上了鎖,就可以知道沒有其他線程能夠訪問該對象。即使在搶占式模型中,其他線程也不能夠訪問此對象,直到上鎖的線程被喚醒、完成工作並開鎖。那些試圖訪問一個上鎖對象的線程通常會進入睡眠狀態,直到上鎖的線程開鎖。一旦鎖被打開,這些睡眠進程就會被喚醒並移到預備就緒隊列中。

在 Java 編程中,所有的對象都有鎖。線程可以使用 synchronized 要害字來獲得鎖。在任一時刻對于給定的類的實例,方法或同步的代碼塊只能被一個線程執行。這是因爲代碼在執行之前要求獲得對象的鎖。繼續我們關于複印機的比喻,爲了避免複印沖突,我們可以簡單地對複印資源實行同步。如同下列的代碼例子,任一時刻只答應一位職員使用複印資源。通過使用方法(在 Copier 對象中)來修改複印機狀態。這個方法就是同步方法。只有一個線程能夠執行一個 Copier 對象中同步代碼,因此那些需要使用 Copier 對象的職員就必須排隊等候。

class CopyMachine {

public synchronized void makeCopies(Document d, int nCopies) {

//only one thread executes this at a time

}

public void loadPaper() {

//multiple threads could Access this at once!

synchronized(this) {

//only one thread accesses this at a time

//feel free to use shared resources, overwrite members, etc.

}

}

}

Fine-grain 鎖

在對象級使用鎖通常是一種比較粗糙的方法。

  幾乎所有使用 AWT 或 Swing 編寫的畫圖程序都需要多線程。但多線程程序會造成許多困難,剛開始編程的開發者經常會發現他們被一些問題所折磨,例如不正確的程序行爲或死鎖。 在本文中,我們將探討使用多線程時碰到的問題,並提出那些常見陷阱的解決方案。 線程是什麽? 一個程序或進程能夠包含多個線程,這些線程可以根據程序的代碼執行相應的指令。多線程看上去似乎在並行執行它們各自的工作,就像在一台計算機上運行著多個處理機一樣。在多處理機計算機上實現多線程時,它們確實可以並行工作。和進程不同的是,線程共享地址空間。也就是說,多個線程能夠讀寫相同的變量或數據結構。 編寫多線程程序時,你必須注重每個線程是否幹擾了其他線程的工作。可以將程序看作一個辦公室,假如不需要共享辦公室資源或與其他人交流,所有職員就會獨立並行地工作。某個職員若要和其他人交談,當且僅當該職員在「聽」且他們兩說同樣的語言。此外,只有在複印機空閑且處于可用狀態(沒有僅完成一半的複印工作,沒有紙張阻塞等問題)時,職員才能夠使用它。在這篇文章中你將看到,在 Java 程序中互相協作的線程就似乎是在一個組織良好的機構中工作的職員。 在多線程程序中,線程可以從預備就緒隊列中得到,並在可獲得的系統 CPU 上運行。操作系統可以將線程從處理器移到預備就緒隊列或阻塞隊列中,這種情況可以認爲是處理器「挂起」了該線程。同樣,Java 虛擬機 (JVM) 也可以控制線程的移動??在協作或搶先模型中??從預備就緒隊列中將進程移到處理器中,于是該線程就可以開始執行它的程序代碼。 協作式線程模型答應線程自己決定什麽時候放棄處理器來等待其他的線程。程序開發員可以精確地決定某個線程何時會被其他線程挂起,答應它們與對方有效地合作。缺點在于某些惡意或是寫得不好的線程會消耗所有可獲得的 CPU 時間,導致其他線程「饑餓」。 在搶占式線程模型中,操作系統可以在任何時候打斷線程。通常會在它運行了一段時間(就是所謂的一個時間片)後才打斷它。這樣的結果自然是沒有線程能夠不公平地長時間霸占處理器。然而,隨時可能打斷線程就會給程序開發員帶來其他麻煩。同樣使用辦公室的例子,假設某個職員搶在另一人前使用複印機,但打印工作在未完成的時候離開了,另一人接著使用複印機時,該複印機上可能就還有先前那名職員留下來的資料。搶占式線程模型要求線程正確共享資源,協作式模型卻要求線程共享執行時間。由于 JVM 規範並沒有非凡規定線程模型,Java 開發員必須編寫可在兩種模型上正確運行的程序。在了解線程以及線程間通訊的一些方面之後,我們可以看到如何爲這兩種模型設計程序。 線程和 Java 語言 爲了使用 Java 語言創建線程,你可以生成一個 Thread 類(或其子類)的對象,並給這個對象發送 start() 消息。(程序可以向任何一個派生自 Runnable 接口的類對象發送 start() 消息。)每個線程動作的定義包含在該線程對象的 run() 方法中。run 方法就相當于傳統程序中的 main() 方法;線程會持續運行,直到 run() 返回爲止,此時該線程便死了。 上鎖 大多數應用程序要求線程互相通信來同步它們的動作。在 Java 程序中最簡單實現同步的方法就是上鎖。爲了防止同時訪問共享資源,線程在使用資源的前後可以給該資源上鎖和開鎖。假想給複印機上鎖,任一時刻只有一個職員擁有鑰匙。若沒有鑰匙就不能使用複印機。給共享變量上鎖就使得 Java 線程能夠快速方便地通信和同步。某個線程若給一個對象上了鎖,就可以知道沒有其他線程能夠訪問該對象。即使在搶占式模型中,其他線程也不能夠訪問此對象,直到上鎖的線程被喚醒、完成工作並開鎖。那些試圖訪問一個上鎖對象的線程通常會進入睡眠狀態,直到上鎖的線程開鎖。一旦鎖被打開,這些睡眠進程就會被喚醒並移到預備就緒隊列中。 在 Java 編程中,所有的對象都有鎖。線程可以使用 synchronized 要害字來獲得鎖。在任一時刻對于給定的類的實例,方法或同步的代碼塊只能被一個線程執行。這是因爲代碼在執行之前要求獲得對象的鎖。繼續我們關于複印機的比喻,爲了避免複印沖突,我們可以簡單地對複印資源實行同步。如同下列的代碼例子,任一時刻只答應一位職員使用複印資源。通過使用方法(在 Copier 對象中)來修改複印機狀態。這個方法就是同步方法。只有一個線程能夠執行一個 Copier 對象中同步代碼,因此那些需要使用 Copier 對象的職員就必須排隊等候。 class CopyMachine { public synchronized void makeCopies(Document d, int nCopies) { //only one thread executes this at a time } public void loadPaper() { //multiple threads could Access this at once! synchronized(this) { //only one thread accesses this at a time //feel free to use shared resources, overwrite members, etc. } } } Fine-grain 鎖 在對象級使用鎖通常是一種比較粗糙的方法。
󰈣󰈤
王朝萬家燈火計劃
期待原創作者加盟
 
 
 
>>返回首頁<<
 
 
 
 
 熱帖排行
 
 
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有