Java 多線程編程

Java 多線程編程

Java 給多線程編程提供了內置的支持。 一條線程指的是進程中一個單一順序的控制流,一個進程中能夠併發多個線程,每條線程並行執行不一樣的任務。
多線程是多任務的一種特別的形式,但多線程使用了更小的資源開銷。
這裏定義和線程相關的另外一個術語 - 進程:一個進程包括由操做系統分配的內存空間,包含一個或多個線程。一個線程不能獨立的存在,它必須是進程的一部分。一個進程一直運行,直到全部的非守護線程都結束運行後才能結束。
多線程能知足程序員編寫高效率的程序來達到充分利用 CPU 的目的。java

1. 一個線程的生命週期

線程是一個動態執行的過程,它也有一個從產生到死亡的過程。程序員

下圖顯示了一個線程完整的生命週期。編程

線程的生命週期

  • 新建狀態: 使用 new 關鍵字和 Thread 類或其子類創建一個線程對象後,該線程對象就處於新建狀態。它保持這個狀態直到程序 start() 這個線程。
  • 就緒狀態: 當線程對象調用了start()方法以後,該線程就進入就緒狀態。就緒狀態的線程處於就緒隊列中,要等待JVM裏線程調度器的調度。
  • 運行狀態 若是就緒狀態的線程獲取 CPU 資源,就能夠執行 run(),此時線程便處於運行狀態。處於運行狀態的線程最爲複雜,它能夠變爲阻塞狀態、就緒狀態和死亡狀態。
  • 阻塞狀態 若是一個線程執行了sleep(睡眠)、suspend(掛起)等方法,失去所佔用資源以後,該線程就從運行狀態進入阻塞狀態。在睡眠時間已到或得到設備資源後能夠從新進入就緒狀態。能夠分爲三種:
    • 等待阻塞:運行狀態中的線程執行 wait() 方法,使線程進入到等待阻塞狀態。
    • 同步阻塞:線程在獲取 synchronized 同步鎖失敗(由於同步鎖被其餘線程佔用)。
    • 其餘阻塞:經過調用線程的 sleep() 或 join() 發出了 I/O 請求時,線程就會進入到阻塞狀態。當sleep() 狀態超時,join() 等待線程終止或超時,或者 I/O 處理完畢,線程從新轉入就緒狀態。
  • 死亡狀態: 一個運行狀態的線程完成任務或者其餘終止條件發生時,該線程就切換到終止狀態。

2. 線程的優先級

每個 Java 線程都有一個優先級,這樣有助於操做系統肯定線程的調度順序。多線程

Java 線程的優先級是一個整數,其取值範圍是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。併發

默認狀況下,每個線程都會分配一個優先級 NORM_PRIORITY(5)。測試

具備較高優先級的線程對程序更重要,而且應該在低優先級的線程以前分配處理器資源。可是,線程優先級不能保證線程執行的順序,並且很是依賴於平臺。操作系統


3. 建立一個線程

Java 提供了三種建立線程的方法:線程

  • 經過實現 Runnable 接口;
  • 經過繼承 Thread 類自己;
  • 經過 Callable 和 Future 建立線程。

4. 經過實現 Runnable 接口來建立線程

建立一個線程,最簡單的方法是建立一個實現 Runnable 接口的類。code

爲了實現 Runnable,一個類只須要執行一個方法調用 run(),聲明以下:對象

public void run()

你能夠重寫該方法,重要的是理解的 run() 能夠調用其餘方法,使用其餘類,並聲明變量,就像主線程同樣。

在建立一個實現 Runnable 接口的類以後,你能夠在類中實例化一個線程對象。

Thread 定義了幾個構造方法,下面的這個是咱們常用的:

Thread(Runnable threadOb,String threadName);

這裏,threadOb 是一個實現 Runnable 接口的類的實例,而且 threadName 指定新線程的名字。

新線程建立以後,你調用它的 start() 方法它纔會運行。

void start();

5. 經過繼承Thread來建立線程

建立一個線程的第二種方法是建立一個新的類,該類繼承 Thread 類,而後建立一個該類的實例。

繼承類必須重寫 run() 方法,該方法是新線程的入口點。它也必須調用 start() 方法才能執行。

該方法儘管被列爲一種多線程實現方式,可是本質上也是實現了 Runnable 接口的一個實例。


6.Thread 方法

下表列出了Thread類的一些重要方法:

序號 方法描述
1 public void start() 使該線程開始執行;Java 虛擬機調用該線程的 run 方法。
2 public void run() 若是該線程是使用獨立的 Runnable 運行對象構造的,則調用該 Runnable 對象的 run 方法;不然,該方法不執行任何操做並返回。
3 public final void setName(String name) 改變線程名稱,使之與參數 name 相同。
4 public final void setPriority(int priority) 更改線程的優先級。
5 public final void setDaemon(boolean on) 將該線程標記爲守護線程或用戶線程。
6 public final void join(long millisec) 等待該線程終止的時間最長爲 millis 毫秒。
7 public void interrupt() 中斷線程。
8 public final boolean isAlive() 測試線程是否處於活動狀態。

測試線程是否處於活動狀態。 上述方法是被Thread對象調用的。下面的方法是Thread類的靜態方法。

序號 方法描述
1 public static void yield() 暫停當前正在執行的線程對象,並執行其餘線程。
2 public static void sleep(long millisec) 在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行),此操做受到系統計時器和調度程序精度和準確性的影響。
3 public static boolean holdsLock(Object x) 當且僅當當前線程在指定的對象上保持監視器鎖時,才返回 true。
4 public static Thread currentThread() 返回對當前正在執行的線程對象的引用。
5 public static void dumpStack() 將當前線程的堆棧跟蹤打印至標準錯誤流。

6. 經過 Callable 和 Future 建立線程

    1. 建立 Callable 接口的實現類,並實現 call() 方法,該 call() 方法將做爲線程執行體,而且有返回值。
    1. 建立 Callable 實現類的實例,使用 FutureTask 類來包裝 Callable 對象,該 FutureTask 對象封裝了該 Callable 對象的 call() 方法的返回值。
    1. 使用 FutureTask 對象做爲 Thread 對象的 target 建立並啓動新線程。
    1. 調用 FutureTask 對象的 get() 方法來得到子線程執行結束後的返回值。

7. 線程的幾個主要概念

在多線程編程時,你須要瞭解如下幾個概念:

  • 線程同步
  • 線程間通訊
  • 線程死鎖
  • 線程控制:掛起、中止和恢復

8. 多線程的使用

有效利用多線程的關鍵是理解程序是併發執行而不是串行執行的。例如:程序中有兩個子系統須要併發執行,這時候就須要利用多線程編程。

經過對多線程的使用,能夠編寫出很是高效的程序。不過請注意,若是你建立太多的線程,程序執行的效率其實是下降了,而不是提高了。

請記住,上下文的切換開銷也很重要,若是你建立了太多的線程,CPU 花費在上下文的切換的時間將多於執行程序的時間!

相關文章
相關標籤/搜索