Java 多執行緒編程
Java給了多執行緒程式設計內建的支援。一個多執行緒程式包含兩個或多個能並發運行的部分。程式的每一部分都稱作一個線程,並且每個線程定義了一個獨立的執行路徑。
多執行緒是多任務的一種特別的形式,但多執行緒使用了更小的資源開銷。
這裡定義和執行緒相關的另一個術語 - 行程:一個行程包括由作業系統分配的記憶體空間,包含一個或多個執行緒。一個執行緒不能獨立的存在,它必須是行程的一部分。一個行程一直運行,直到所有的非守候執行緒都結束運行後才能結束。
多執行緒能滿足程式設計師編寫高效率的程式來達到充分利用CPU的目的。
一個執行緒的生命週
執行緒經過其生命週期的各個階段。下圖顯示了一個線程完整的生命週期。
新狀態:
#使用new 關鍵字與 Thread 類別或其子類別建立一個執行緒物件後,該執行緒物件就處於新建狀態。它保持這個狀態直到程式 start() 這個線程。
就緒狀態:
當執行緒物件呼叫了start()方法之後,該執行緒就進入就緒狀態。就緒狀態的執行緒處於就緒佇列中,要等待JVM裡執行緒調度器的調度。
運行狀態:
如果就緒狀態的執行緒取得CPU 資源,就可以執行run(),此時執行緒便處於運作狀態。處於運作狀態的執行緒最為複雜,它可以變成阻塞狀態、就緒狀態和死亡狀態。
阻塞狀態:
如果一個執行緒執行了sleep(睡眠)、suspend(掛起)等方法,失去所佔用資源之後,該執行緒就從運行狀態進入阻塞狀態。在睡眠時間已到或獲得設備資源後可以重新進入就緒狀態。
死亡狀態:
一個運行狀態的執行緒完成任務或其他終止條件發生時,執行緒就切換到終止狀態。
執行緒的優先權
每一個Java執行緒都有一個優先權,這有助於作業系統決定執行緒的排程順序。
Java執行緒的優先權是一個整數,其取值範圍是1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。
預設情況下,每個執行緒都會指派一個優先權NORM_PRIORITY(5)。
具有較高優先權的執行緒對程式更重要,並且應該在低優先權的執行緒之前分配處理器資源。但是,執行緒優先權不能保證執行緒執行的順序,而且非常依賴平台。
建立一個執行緒
Java提供了兩種建立執行緒方法:
#透過實作Runable介面;
#透過繼承Thread類別本身。
透過實作Runnable介面來建立線程
建立一個線程,最簡單的方法是建立一個實作Runnable介面的類別。
為了實作Runnable,一個類別只需要執行一個方法呼叫run(),宣告如下:
public void run()
你可以重寫該方法,重要的是理解的run()可以呼叫其他方法,使用其他類,並聲明變量,就像主線程一樣。
在建立一個實作Runnable介面的類別之後,你可以在類別中實例化一個執行緒物件。
Thread定義了幾個建構方法,下面的這個是我們常用的:
Thread(Runnable threadOb,String threadName);
這裡,threadOb 是實作Runnable 介面的類別的實例,並且threadName指定新執行緒的名字。
新執行緒建立之後,你呼叫它的start()方法它才會運作。
void start();
實例
下面是一個建立執行緒並開始讓它執行的實例:
// 创建一个新的线程 class NewThread implements Runnable { Thread t; NewThread() { // 创建第二个新线程 t = new Thread(this, "Demo Thread"); System.out.println("Child thread: " + t); t.start(); // 开始线程 } // 第二个线程入口 public void run() { try { for(int i = 5; i > 0; i--) { System.out.println("Child Thread: " + i); // 暂停线程 Thread.sleep(50); } } catch (InterruptedException e) { System.out.println("Child interrupted."); } System.out.println("Exiting child thread."); } } public class ThreadDemo { public static void main(String args[]) { new NewThread(); // 创建一个新线程 try { for(int i = 5; i > 0; i--) { System.out.println("Main Thread: " + i); Thread.sleep(100); } } catch (InterruptedException e) { System.out.println("Main thread interrupted."); } System.out.println("Main thread exiting."); } }
編譯以上程式執行結果如下:
Child thread: Thread[Demo Thread,5,main] Main Thread: 5 Child Thread: 5 Child Thread: 4 Main Thread: 4 Child Thread: 3 Child Thread: 2 Main Thread: 3 Child Thread: 1 Exiting child thread. Main Thread: 2 Main Thread: 1 Main thread exiting.
#透過繼承Thread來建立執行緒
建立一個執行緒的第二種方法是建立一個新的類,該類別繼承Thread類,然後建立一個該類別的實例。
繼承類別必須重寫run()方法,該方法是新執行緒的入口點。它也必須呼叫start()方法才能執行。
實例
// 通过继承 Thread 创建线程 class NewThread extends Thread { NewThread() { // 创建第二个新线程 super("Demo Thread"); System.out.println("Child thread: " + this); start(); // 开始线程 } // 第二个线程入口 public void run() { try { for(int i = 5; i > 0; i--) { System.out.println("Child Thread: " + i); // 让线程休眠一会 Thread.sleep(50); } } catch (InterruptedException e) { System.out.println("Child interrupted."); } System.out.println("Exiting child thread."); } } public class ExtendThread { public static void main(String args[]) { new NewThread(); // 创建一个新线程 try { for(int i = 5; i > 0; i--) { System.out.println("Main Thread: " + i); Thread.sleep(100); } } catch (InterruptedException e) { System.out.println("Main thread interrupted."); } System.out.println("Main thread exiting."); } }
編譯以上程式執行結果如下:
Child thread: Thread[Demo Thread,5,main] Main Thread: 5 Child Thread: 5 Child Thread: 4 Main Thread: 4 Child Thread: 3 Child Thread: 2 Main Thread: 3 Child Thread: 1 Exiting child thread. Main Thread: 2 Main Thread: 1 Main thread exiting.
Thread 方法
下表列出了Thread類別的一些重要方法:
序號 | 方法說明 |
---|---|
public void start()#
使該執行緒開始執行; Java 虛擬機器呼叫該執行緒的 run 方法。 | |
public void run()#
如果該執行緒是使用獨立的 Runnable 來執行物件建構的,則呼叫該 Runnable 物件的 run 方法;否則,該方法不執行任何操作並傳回。 | |
public final void setName(String name)
改變線程名稱,使其與參數 name 相同。 | |
public final void setPriority(int priority)
更改執行緒的優先權。 | |
public final void setDaemon(boolean on)#
將該線程標記為守護線程或使用者線程。 | |
public final void join(long millisec)#
等待該執行緒終止的時間最長為 millis 毫秒。 | |
public void interrupt()
中斷線程。 | |
public final boolean isAlive()
測試線程是否處於活動狀態。 |
序號 | # 說明 |
---|---|
public static void yield()
暫停目前正在執行的線程對象,並執行其他線程。 | |
public static void sleep(long millisec)#
在指定的毫秒數內讓目前正在執行的執行緒休眠(暫停執行),此操作受到系統計時器和調度程序精度和準確性的影響。 | |
public static boolean holdsLock(Object x)#
當且僅噹噹前執行緒在指定的物件上保持監視器鎖定時,才傳回 true。 | |
public static Thread currentThread()#
傳回對目前正在執行的線程物件的參考。 | |
public static void dumpStack()
將目前執行緒的堆疊追蹤列印至標準錯誤流。 |
// 文件名 : DisplayMessage.java // 通过实现 Runnable 接口创建线程 public class DisplayMessage implements Runnable { private String message; public DisplayMessage(String message) { this.message = message; } public void run() { while(true) { System.out.println(message); } } }
// 文件名 : GuessANumber.java // 通过继承 Thread 类创建线程 public class GuessANumber extends Thread { private int number; public GuessANumber(int number) { this.number = number; } public void run() { int counter = 0; int guess = 0; do { guess = (int) (Math.random() * 100 + 1); System.out.println(this.getName() + " guesses " + guess); counter++; }while(guess != number); System.out.println("** Correct! " + this.getName() + " in " + counter + " guesses.**"); } }
// 文件名 : ThreadClassDemo.java public class ThreadClassDemo { public static void main(String [] args) { Runnable hello = new DisplayMessage("Hello"); Thread thread1 = new Thread(hello); thread1.setDaemon(true); thread1.setName("hello"); System.out.println("Starting hello thread..."); thread1.start(); Runnable bye = new DisplayMessage("Goodbye"); Thread thread2 = new Thread(bye); thread2.setPriority(Thread.MIN_PRIORITY); thread2.setDaemon(true); System.out.println("Starting goodbye thread..."); thread2.start(); System.out.println("Starting thread3..."); Thread thread3 = new GuessANumber(27); thread3.start(); try { thread3.join(); }catch(InterruptedException e) { System.out.println("Thread interrupted."); } System.out.println("Starting thread4..."); Thread thread4 = new GuessANumber(75); thread4.start(); System.out.println("main() is ending..."); } }執行結果如下,每一次執行的結果都不一樣。
Starting hello thread... Starting goodbye thread... Hello Hello Hello Hello Hello Hello Hello Hello Hello Thread-2 guesses 27 Hello ** Correct! Thread-2 in 102 guesses.** Hello Starting thread4... Hello Hello ..........remaining result produced.執行緒的幾個主要概念:在多執行緒程式設計時,你需要了解以下幾個概念:
- 執行緒同步
- 執行緒間通訊
- #執行緒死鎖
- 執行緒控制:掛起、停止和復原
多執行緒的使用#有效利用多執行緒的關鍵是理解程式是並發執行而不是串列執行的。例如:程式中有兩個子系統需要並發執行,這時候就需要利用多執行緒程式設計。 透過對多執行緒的使用,可以寫出非常有效率的程式。不過請注意,如果你創建太多的線程,程式執行的效率實際上是降低了,而不是提升了。 請記住,上下文的切換開銷也很重要,如果你創建了太多的線程,CPU花費在上下文的切換的時間將多於執行程式的時間!