Java的多執行緒,由基礎開始認識Threading

一個應用程式或行程(Process)可以向作業系統取得多個執行緒(threads),多個執行緒可以模擬出平行處理的效果。一個具備單一(此處的CPU僅考慮單核心)的系統,事實上同一時間只能執行一件事,但如果將一個CPU的執行時間切割為很小的單位,將這些單位分給多個行程去使用,就可以模擬出多工(multi-task)的效果,如下圖。

一個可以執行的Java類別,在main這個特別的方法被執行時,也就擁有了作業系統的一個執行緒,由main方法的開始到結束,都使用這個單一執行緒。因此,假如我想設計一個賽馬的程式,就只能讓一匹馬從起點跑到終點,如果想要設計出多匹馬一起跑,而且各自跑到終點,那就要使用多執行緒的功能。
Java語言中,java.lang.Thread類別就是設計讓使用者向作業系統取得額外的執行緒,並在這個執行緒類別中,放入設計師想要做的工作,開發人員只需要繼承Thread類別即可讓新類別具備執行緒的能力。另外,若我們設計的類別已經繼承了某類別時,因Java語言單一繼承的限制性,而無法繼承Thread類別時,則可以利用java.lang.Runnable這個介面達到多執行緒的目的,本系統文章後續將說明如何利用Thread類別與Runnable介面來設計多執行緒的程式。
為了要展示執行緒的效果,有時會在程式碼中使用大量的迴圈讓它不要執行慢一點,好看得出它的效用。當然,在練習時可以這麼做,但這只會在執行時增加不必要的CPU負荷。若要有停頓效果,應使用讓執行緒暫停的方法,如sleep(50)可讓執行緒進入休眠50毫秒,刻意使執行速度放慢,才能看出其差異。

執行緒

一個程式在執行過程中可以擁多個執行緒,這些執行緒可以由開發人員決定而擁有不同層級的優先權(priority),而且,每個執行緒都有四種狀態:

  • 執行中(Running)
    • 執行緒正在執行當中。
  • 暫停(Suspended)
    • 暫停已在執行當中的執行緒,但可以讓它繼續執行(resume)。
  • 被阻擋(Blocked)
    • 執行緒因某個特定的資源被其他執行緒占用時,會進入等待的狀態。
  • 終止(Terminated)
    • 執行緒被終止或停止,但無法再繼續執行。

java.lang.Thread類別的方法

可以利用繼承Thread類別或實作Runnable介面來產生執行緒,Thread類別設計了許多管理執行緒的方法,常用的方法有:

  • String getName()
    • 取得執行緒的名稱
  • void run()
    • 執行緒的主要工作內容
  • void start()
    • 啟動執行緒的方法
  • int getPriority()
    • 取得執行緒的優先權值
  • boolean isAlive()
    • 執行緒是否存活著?
  • void join()
    • 等待執行緒結束,進入終止狀態
  • void sleep(long millis)
    • 使執行緒進入睡眠狀態,傳入值為睡眠時間,單位是毫秒

main方法也是個執行緒

每個Java應用程式都擁有一個執行緒,因為main方法執行時,一定會產生一個執行緒(main thread),讀者可以在main方法區塊中,利用Thread類別的方法currentThread()便可得到目前的執行緒物件,如下程式範例,可取得執行緒的名稱(getName()),或改變該執行緒的名稱(setName()方法)。

public class ThreadName {
    public static void main(String[] args) {
        Thread thr = Thread.currentThread();
        System.out.println("目前執行緒名稱:"+thr.getName());
        thr.setName("DEMO");
        System.out.println("更改後的名稱:"+thr.getName());
    }
}

執行結果:

目前執行緒名稱:main
更改後的名稱:DEMO

兩匹馬假賽跑

若只有使用main方法來設計賽馬的程式,不能達到實際的需求,以下的程式利用for迴圈讓兩匹馬(整數h1與h2)從0加到5000,看那匹馬先到終點(5000):

public class RacingNG {
    public static void main(String[] args) {
        int h1 = 0;
        int h2 = 0;
        for (int i=0; i<5000; i++){
            h1++;
            h2++;
            System.out.println("H1:"+h1);
            System.out.println("H2:"+h2);
        }
    }
}

執行結果,每一次執行一定是h1先到達終點:

(省略)
H1:4996
H2:4996
H1:4997
H2:4997
H1:4998
H2:4998
H1:4999
H2:4999
H1:5000
H2:5000

每次執行結果都一樣是不符合實際需求的,因此,我們應該把h2的工作(從0加到5000)「搬到」另一個執行緒去處理,讓main執行緒與新執行緒一起執行,要達到這個功能,我們需要的是Thread類別。

如何使用java.lang.Thread類別來產生第二個執行緒呢? 下一篇將從【繼承Thread類別】開始實作一個賽馬程式。
下一篇: Java執行緒,以賽馬為例,學習如何繼承Thread與實作Runnable

版權聲明
本文章授權範圍僅限綠豆湯網站使用,除Facebook之類社群等未更改本文章出處之分享行為不在此限,其他個人或公司未經作者同意,不得任意將本文章內容轉載至其他網站,或以任何形式重製,為以免觸犯著作權法,請尊重作者之智慧財產權。

2 Comments

  1. allen

    請問繼承 thread與 Runnable使用區別,何時該用Thread ,又何時該實作 Runnable

    • 如果你想要變成執行緒的類別(不是全新的)原本已繼承父類別了,那就只好實作Runnable了。

發佈回覆給「allen」的留言 取消回覆

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *