Java中的線程生命周期核心概念

前言:

在本文中,我們將詳細討論Java中的一個核心概念——線程的生命周期。我們將使用一個快速的圖解,當然還有實用的代碼片段來更好地理解線程執行期間的這些狀態。

Java多線程

在Java語言中,多線程是由線程的核心概念驅動的。線程在其生命周期中會經歷各種狀態:

Java中線程的生命周期

java.lang.Thread類包含一個靜態枚舉,它定義瞭它的潛在狀態。在任何給定的時間點內,線程隻能處於以下狀態之一:

  • NEW – 一個新創建的線程,尚未開始執行
  • RUNNABLE – 正在運行或準備執行,但它正在等待資源分配
  • BLOCKED – 等待獲取監視器鎖以進入或重新進入同步塊/方法
  • WAITING – 等待其他線程執行特定操作,無任何時間限制
  • TIMED_WAITING – 等待其他線程在指定時間段內執行特定操作
  • TERMINATED – 已完成執行

上圖涵蓋瞭所有這些狀態;現在讓我們詳細討論其中的每一項。

NEW

新線程(或出生線程)是已創建但尚未啟動的線程。在我們使用start()方法啟動它之前,它一直保持此狀態。

以下代碼段顯示瞭新創建的處於新狀態的線程:

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

由於我們尚未啟動上述線程,因此方法t.getState()會打印:

NEW

Runnable

當我們創建瞭一個新線程並對其調用start()方法時,它將從NEW狀態移動到RUNNABLE狀態。處於此狀態的線程正在運行或準備運行,但它們正在等待來自系統的資源分配。

在多線程環境中,線程調度程序(JVM的一部分)為每個線程分配固定的時間量。因此,它會運行一段特定的時間,然後將控制權交給其他可運行的線程。

例如,讓我們將t.start()方法添加到前面的代碼中,並嘗試訪問其當前狀態:

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

此代碼很可能返回以下輸出:

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) {
            // Infinite loop to mimic heavy processing
            // 't1' won't leave this method
            // when 't2' try to enter this
        }
    }
}

在此代碼中:

  • 我們創建瞭兩個不同的線程—t1t2
  • t1啟動並進入synchronized commonResource()方法;這意味著隻有一個線程可以訪問它;在當前線程完成處理之前,將阻止嘗試訪問此方法的所有其他後續線程進一步執行
  • t1進入此方法時,它將保持在無限while循環中;這隻是為瞭模擬繁重的處理,以便所有其他線程都無法進入此方法
  • 現在,當我們啟動t2時,它嘗試進入commonResource()方法,t1已經訪問瞭該方法,因此t2將保持在阻塞狀態

處於這種狀態,我們稱之為t2.getState()並獲取輸出,如下所示:

BLOCKED

Waiting

線程在等待其他線程執行特定操作時處於等待狀態。

根據JavaDocs,任何線程都可以通過調用以下三種方法中的任何一種進入這種狀態:

  • object.wait()
  • thread.join()
  • LockSupport.park()

請註意,在wait()join()中,我們沒有定義任何超時時間,因為下一節將介紹該場景。

現在,讓我們嘗試重現這種狀態:

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創建t2並啟動它
  • t2的處理繼續時,我們稱之為t2.join(),這會使t1處於等待狀態,直到t2完成執行
  • 因為t1正在等待t2完成,所以我們調用t1.getState()來自t2

正如您所期望的那樣,這裡的輸出是:

WAITING

Timed Waiting

當線程等待另一個線程在規定的時間內執行特定操作時,該線程處於TIMED_WAITING狀態。

根據JavaDocs,有五種方法可以將線程置於TIMED_WAITING狀態:

  • thread.sleep(long millis)
  • wait(int timeout) 或 wait(int timeout, int nanos)
  • thread.join(long millis)
  • 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();
        
        // The following sleep will give enough time for ThreadScheduler
        // to start processing of thread 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);
        }
    }
}

這裡,我們創建並啟動瞭一個線程t1,該線程進入睡眠狀態,超時時間為5秒;

輸出將為:

TIMED_WAITING

Terminated

這是死線程的狀態。當它完成執行或異常終止時,它處於終止狀態。

讓我們在以下示例中嘗試實現此狀態:

public class TerminatedState implements Runnable {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new TerminatedState());
        t1.start();
        // The following sleep method will give enough time for 
        // thread t1 to complete
        Thread.sleep(1000);
        Log.info(t1.getState());
    }
    
    @Override
    public void run() {
        // No processing in this block
    }
}

在這裡,雖然我們已經啟動瞭線程t1,但它是下一個語句Thread.sleep(1000)為t1提供瞭足夠的時間來完成,因此該程序為我們提供如下輸出:

TERMINATED

除瞭線程狀態之外,我們還可以檢查isAlive()方法以確定線程是否處於活動狀態。例如,如果我們在此線程上調用isAlive()方法:

Assert.assertFalse(t1.isAlive());

結論

在本文中,我們學習瞭Java中線程的生命周期。我們查看瞭線程定義的所有六個狀態。陳述enum並用快速示例再現它們。雖然代碼片段在幾乎每臺機器上都會給出相同的輸出,但在某些例外情況下,我們可能會得到一些不同的輸出,因為無法確定線程調度器的確切行為。

到此這篇關於Java中的線程生命周期核心概念的文章就介紹到這瞭,更多相關Java線程生命周期內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: