1. 程式人生 > 實用技巧 >Java執行緒的生命週期

Java執行緒的生命週期

目錄

概覽

Java語言中,多執行緒是由執行緒的核心概念驅動的,而執行緒的生命週期會經歷以下的不同狀態。

Java java.lang.Thread類包含了一個靜態類State,它定義了執行緒的狀態,在任意一個時間點上,執行緒只可能是其中的一個狀態,我們接下來逐一討論每個狀態。

NEW

新建立且還未被執行的執行緒

在我們呼叫start()方法之前當前執行緒將一直保持該狀態:

Runnable runnable = new NewState();
Thread t = new Thread(runnable);
log.info(t.getState());

由於我們還未呼叫start()方法,最後將列印

NEW

RUNNABLE

當前執行緒正在執行或處於就緒狀態(等待CPU分配執行時間)

當我們呼叫start()方法之後,執行緒狀態將由NEW狀態切換到RUNABLE狀態。

多執行緒環境下,執行緒排程器(屬於JVM管轄)分配一定的時間給每個執行緒,當執行一定時間後將控制權交給其他可執行執行緒。

我們現在在上面的例子後面加上執行start()方法的程式碼再次獲取一下當前的狀態:

Runnable runnable = new NewState();
Thread t = new Thread(runnable);
t.start();
log.info(t.getState());

執行一下結果很有可能輸出如下:

RUNNABLE

注意我們不能保證肯定會輸出RUNNABLE狀態,因為當我們呼叫t.getState()方法時,執行緒有可能執行完成了。

BLOCKED

等待獲取到一個排他鎖,這個事件將在另外一個執行緒放棄這個鎖的時候發生,之後當前執行緒進入同步區域

我們來看個例子:

public class BlockedState {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new DemoThreadB());
        Thread t2 = new Thread(new DemoThreadB());
         
        t1.start();
        t2.start();
         
        Thread.sleep(1000);
        log.info(t2.getState());
        System.exit(0);
    }
}
 
class DemoThreadB implements Runnable {
    @Override
    public void run() {
        commonResource();
    }
     
    public static synchronized void commonResource() {
        while(true) {
            // 無限迴圈來模擬耗時操作
            // t1執行緒不會退出當前方法,t2執行緒無法進入
        }
    }
}

這段程式碼中,執行緒t1執行後進入到commonResource()同步方法中,這個一個排他鎖,所有其他想進入該方法的執行緒都會被阻塞直到當前執行緒退出該方法。這時候執行緒t2也運行了,也需要訪問commonResource()同步方法,但是由於執行緒t1在該方法中無限迴圈沒有退出,t2將會一直保持BLOCKED狀態,最後程式會列印:

BLOCKED

WAITING

當前執行緒不會被分配CPU執行時間,需被其他執行緒顯示的喚醒

根據Java文件上解釋,只有以下方法會讓執行緒進入無限期的等待狀態:

  • 沒有設定timeout引數的Object.wait()方法
  • 沒有設定timeout引數的Thread.join()方法
  • LockSupport.park()方法

後面會另寫一篇文章詳細討論wait()、notify() 、notifyAll(),下面面子先展示join()方法呼叫後執行緒進入WAITING狀態:

public class WaitingState implements Runnable {
    public static Thread t1;
 
    public static void main(String[] args) {
        t1 = new Thread(new WaitingState());
        t1.start();
    }
 
    public void run() {
        Thread t2 = new Thread(new DemoThreadWS());
        t2.start();
 
        try {
            t2.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("Thread interrupted", e);
        }
    }
}
 
class DemoThreadWS implements Runnable {
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("Thread interrupted", e);
        }
         
        log.info(WaitingState.t1.getState());
    }
}

這段程式碼中,我們執行主執行緒建立執行緒t1並執行t1,在t1中我們建立執行緒t2並執行t2,在t2執行時,我們在t1使用t2.join()等待t2執行結束,這會讓t1進入WAITING狀態直到t2結束,當t1在等待t2執行結束的時候,我們在t2方法中呼叫t1.getState()來檢視t1的狀態,程式輸入如下:

WAITING

WAITING

當前執行緒也不會被分配CPU執行時間,不過無需被其他執行緒顯示的喚醒,在一定時間之後會被系統自動喚醒

根據Java文件上解釋,有以下5種方法能讓執行緒進入限期等待狀態:

  • Thread.sleep()方法
  • 設定了timeout引數的Object.wait()方法
  • 設定了timeout引數的Thread.join()方法
  • LockSupport.parkNanos()方法
  • LockSupport.parkUntil()方法

我們來看個例子:

public class TimedWaitingState {
    public static void main(String[] args) throws InterruptedException {
        DemoThread obj1 = new DemoThread();
        Thread t1 = new Thread(obj1);
        t1.start();
         
        // 休眠一秒讓執行緒排程器有足夠的時間去執行執行緒t1
        Thread.sleep(1000);
        log.info(t1.getState());
    }
}
 
class DemoThread implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("Thread interrupted", e);
        }
    }
}

例子很簡單,程式輸出如下:

TIMED_WAITING

TERMINATED

當前執行緒已執行結束,已終止狀態

後面會另寫一篇文章詳細討論停止執行緒的不同方式,當前看下面的簡單例子:

public class TerminatedState implements Runnable {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new TerminatedState());
        t1.start();
        
        // 休眠1秒讓執行緒t1有足夠時間執行完畢
        Thread.sleep(1000);
        Log.info(t1.getState());
    }
     
    @Override
    public void run() {
        // 什麼也不做
    }
}

程式輸出如下:

TERMINATED

除了檢視執行緒狀態,我們還可以呼叫isAlive()方法來檢視執行緒是否還活躍:

Assert.assertFalse(t1.isAlive());

當前狀態下它會返回false,簡單來說,執行緒只在呼叫start()後且未結束前活躍。