轉載自 這裡
前言 :
在介紹Thread之前,我們必須先把Program和Process這兩個觀念作一個釐清 :
- Program:一群程式碼的集合,用以解決特定的問題。以物件導向的觀念來類比,相當於Class。
- Process:由Program所產生的執行個體,一個Program可以同時執行多次,產生多個Process。以物件導向的觀念來類比,相當於Object。每一個Process又由以下兩個東西組成
由上面的描述中,我們在歸納Thread的重點如下 :
如何產生Thread :
Java以 java.lang.Thread 這個類別來表示Thread。Class Thread其中有兩個Constructor :
使用Thread()產生的Thread,其進入點為Thread裡的run();使用Thread(Runnable)產生的Thread,其進入點為 Runnable 物件裡的run()。當run()結束時,這個Thread也就結束了;這和main()結束有相同的效果。其用法以下面範例說明 :
以上程式執行後,螢幕上會持續印出"User Created Thread"或"Main Thread"的字樣。利用Runnable的寫法如下 :
Thread的優先權與影響資源的相關方法 :
Thread.setPriority(int)可以設定Thread的優先權,數字越大優先權越高。Thread定義了3個相關的static final variable :
要提醒讀者的是,優先權高的Thread其佔有CPU的機會比較高,但優先權低的也都會有機會執行到。其他有關Thread執行的方法有 :
- yield() : 先讓給別的Thread執行
- sleep(int time) : 休息time mini second(1/1000秒)
- join() : 呼叫ThreadA.join()的執行緒會等到ThreadA結束後,才能繼續執行.
你可以執行下面的程式,看看yield()的效果 :
觀看join的效果 :
Critical Section(關鍵時刻)的保護措施 :
如果設計者沒有提供保護機制的話,Thread取得和失去CPU控制權的時機是由作業系統來決定。也就是說Thread可能在執行任何一個機器指令時,被作業系統取走CPU控制權,並交給另一個Thread。由於某些真實世界的動作是不可分割的,例如跨行轉帳X圓由A帳戶到B帳戶,轉帳前後這兩個帳戶的總金額必須相同,但以程式來實作時,卻無法用一個指令就完成,如轉帳可能要寫成下面的這一段程式碼 :
如果兩個Thread同時要存取A,B兩帳戶進行轉帳,假設當Thread one執行到SUBX後被中斷,Threadtwo接手執行完成另一個轉帳要求,然後Threadone繼續執行未完成的動作,請問這兩個轉帳動作正確嗎?我們以A=1000,B=0,分別轉帳100,200圓來說明此結果 :
你會發現執行完成後A=900,B=300,也就是說銀行平白損失了200圓。當然另外的執行順序可能造成其他不正確的結果。我們把這問題再整理一下
因此在撰寫多執行緒的程式時,必須特別考慮這種狀況(又稱為race condition)。Java的解決辦法是,JVM會在每個物件上擺一把鎖(lock),然後程式設計者可以宣告執行某一段程式(通常是用來存取共同資料結構的程式碼, 又稱為Critical Section)時,必須拿到某物件的鎖才行,這個鎖同時間最多只有一個執行緒可以擁有它.
除了synchronized(ref)的語法可以鎖定ref指到的物件外,synchronized也可以用在object method前面,表示要鎖定this物件才能執行該方法。以下是Queue結構的範例 :
雖然上面的程式正確無誤,但並未考慮資源不足時該如何處理。例如Queue已經沒有資料了,卻還想拿出來;或是Queue裡已經塞滿了資料,使用者卻還要放進去?我們當然可以使用Exception Handling的機制 :
但假設我們的執行環境是,某些Thread專門負責讀取使用者的需求,並把工作放到Queue裡面,某些Thread則專門由Queue裡抓取工作需求做進一步處理。這種架構的好處是,可以把慢速或不定速的輸入(如透過網路讀資料,連線速度可能差很多),和快速的處理分開,可使系統的反應速度更快,更節省資源。那麼以Exceptoin來處理Queue空掉或爆掉的情況並不合適,因為使用Queue的人必須處理例外狀況,並不斷的消耗CPU資源 :
為了解決這類資源分配的問題,Java Object提供了下面三個method :
- wait() :
- notifyAll() : 讓等待該Object的所有Thread進入Runnable Mode。
- notify() : 讓等待該Object的某一個Thread進入Runnable Mode。
所謂Runnable Mode是指該Thread隨時可由作業系統分配CPU資源。Blocking Mode表示該Thread正在等待某個事件發生,作業系統不會讓這種Thread取得CPU資源。前一個Queue的範例就可以寫成 :
補充說明 :
* JDK6 : SwingWorker 使用說明
* [ Java設計模式 ] 多線程設計模式 : Producer-Consumer Pattern (我來做, 你來用)
前言 :
在介紹Thread之前,我們必須先把Program和Process這兩個觀念作一個釐清 :
- Program:一群程式碼的集合,用以解決特定的問題。以物件導向的觀念來類比,相當於Class。
- Process:由Program所產生的執行個體,一個Program可以同時執行多次,產生多個Process。以物件導向的觀念來類比,相當於Object。每一個Process又由以下兩個東西組成
由上面的描述中,我們在歸納Thread的重點如下 :
如何產生Thread :
Java以 java.lang.Thread 這個類別來表示Thread。Class Thread其中有兩個Constructor :
使用Thread()產生的Thread,其進入點為Thread裡的run();使用Thread(Runnable)產生的Thread,其進入點為 Runnable 物件裡的run()。當run()結束時,這個Thread也就結束了;這和main()結束有相同的效果。其用法以下面範例說明 :
- public class ThreadExample1 extends Thread {
- public void run() { // override Thread's run()
- System.out.println("Here is the starting point of Thread.");
- for (;;) { // infinite loop to print message
- System.out.println("User Created Thread");
- }
- }
- public static void main(String[] argv) {
- Thread t = new ThreadExample1(); // 產生Thread物件
- t.start(); // 開始執行t.run()
- for (;;) {
- System.out.println("Main Thread");
- }
- }
- }
- public class ThreadExample2 implements Runnable {
- public void run() { // implements Runnable run()
- System.out.println("Here is the starting point of Thread.");
- for (;;) { // infinite loop to print message
- System.out.println("User Created Thread");
- }
- }
- public static void main(String[] argv) {
- Thread t = new Thread(new ThreadExample2()); // 產生Thread物件
- t.start(); // 開始執行Runnable.run();
- for (;;) {
- System.out.println("Main Thread");
- }
- }
- }
Thread.setPriority(int)可以設定Thread的優先權,數字越大優先權越高。Thread定義了3個相關的static final variable :
- public static final int MAX_PRIORITY 10
- public static final int MIN_PRIORITY 1
- public static final int NORM_PRIORITY 5
- yield() : 先讓給別的Thread執行
- sleep(int time) : 休息time mini second(1/1000秒)
- join() : 呼叫ThreadA.join()的執行緒會等到ThreadA結束後,才能繼續執行.
你可以執行下面的程式,看看yield()的效果 :
觀看join的效果 :
Critical Section(關鍵時刻)的保護措施 :
如果設計者沒有提供保護機制的話,Thread取得和失去CPU控制權的時機是由作業系統來決定。也就是說Thread可能在執行任何一個機器指令時,被作業系統取走CPU控制權,並交給另一個Thread。由於某些真實世界的動作是不可分割的,例如跨行轉帳X圓由A帳戶到B帳戶,轉帳前後這兩個帳戶的總金額必須相同,但以程式來實作時,卻無法用一個指令就完成,如轉帳可能要寫成下面的這一段程式碼 :
- if (A >= X) {
- A = A - X; // 翻譯成3個機器指令LOAD A, SUB X, STORE A
- B = B +X;
- }
你會發現執行完成後A=900,B=300,也就是說銀行平白損失了200圓。當然另外的執行順序可能造成其他不正確的結果。我們把這問題再整理一下
因此在撰寫多執行緒的程式時,必須特別考慮這種狀況(又稱為race condition)。Java的解決辦法是,JVM會在每個物件上擺一把鎖(lock),然後程式設計者可以宣告執行某一段程式(通常是用來存取共同資料結構的程式碼, 又稱為Critical Section)時,必須拿到某物件的鎖才行,這個鎖同時間最多只有一個執行緒可以擁有它.
除了synchronized(ref)的語法可以鎖定ref指到的物件外,synchronized也可以用在object method前面,表示要鎖定this物件才能執行該方法。以下是Queue結構的範例 :
- public class Queue {
- private Object[] data;
- private int size;
- private int head;
- private int tail;
- public Queue(int maxLen) {
- data = new Object[maxLen];
- }
- public synchronized Object deQueue() {
- Object tmp = data[head];
- data[head] = null;
- head = (head+1)%data.length;
- size--;
- return tmp;
- }
- public synchronized void enQueue(Object c) {
- data[tail++] = c;
- tail %= data.length;
- size++;
- }
- }
但假設我們的執行環境是,某些Thread專門負責讀取使用者的需求,並把工作放到Queue裡面,某些Thread則專門由Queue裡抓取工作需求做進一步處理。這種架構的好處是,可以把慢速或不定速的輸入(如透過網路讀資料,連線速度可能差很多),和快速的處理分開,可使系統的反應速度更快,更節省資源。那麼以Exceptoin來處理Queue空掉或爆掉的情況並不合適,因為使用Queue的人必須處理例外狀況,並不斷的消耗CPU資源 :
為了解決這類資源分配的問題,Java Object提供了下面三個method :
- wait() :
- notifyAll() : 讓等待該Object的所有Thread進入Runnable Mode。
- notify() : 讓等待該Object的某一個Thread進入Runnable Mode。
所謂Runnable Mode是指該Thread隨時可由作業系統分配CPU資源。Blocking Mode表示該Thread正在等待某個事件發生,作業系統不會讓這種Thread取得CPU資源。前一個Queue的範例就可以寫成 :
補充說明 :
* JDK6 : SwingWorker 使用說明
* [ Java設計模式 ] 多線程設計模式 : Producer-Consumer Pattern (我來做, 你來用)
This message was edited 14 times. Last update was at 23/02/2011 17:41:47
沒有留言:
張貼留言