1. 程式人生 > >Java並發編程:線程的生命周期是個怎樣的過程?

Java並發編程:線程的生命周期是個怎樣的過程?

object set interrupt rac 接收 err java 資源 getname

前言

在日常開發過程中,如果我們需要執行一些比較耗時的程序的話,一般來說都是開啟一個新線程,把耗時的代碼放在線程裏,然後開啟線程執行。但線程是會耗費系統資源的,如果有多個線程同時運行,互相之間搶占系統資源,那無疑會對系統造成極大的壓力。所以,怎麽操作線程,保證不影響整個應用功能是很重要的,而這就需要我們了解線程的生命周期了。

線程的生命周期

線程的生命周期有6種狀態,分別是NEW(新建)、RUNNABLE(可運行)、BLOCKED(被阻塞)、 WAITING(等待)、TIMED_WAITING(計時等待)、TERMINATED(被終止),在 Thread 源碼的 State 枚舉中都有定義:

public static enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;

    private State() {
    }
}

1、NEW 狀態表示剛剛創建的線程,此時的線程還沒運行,也就是還沒執行start() 方法,創建線程的方式也比較簡單,可以參考《Java並發知識:Java創建線程的三種方式》。

2、當線程執行時,處於 RUNNABLE 狀態,表示線程所需的資源已經準備好了。

3、如果線程在執行的過程中遇到被阻塞的情況,例如線程中的程序中有synchronized 同步代碼塊,線程就會暫停執行,進入阻塞狀態,直至獲取請求的鎖,這時線程就處於 BLOCKED 狀態。

實例代碼如下:

public class ThreadDemo {
    
    public static Object testObject = new Object();

    public static class MyThread extends Thread {

        public MyThread(String name) {
            super.setName(name);
        }

        @Override
        public void run() {
            //每次跑run方法都需要獲取testObject對象
            synchronized (testObject) {
                System.out.println("thread name:" + this.getName());

                //..............耗時操作..............
            }
        }

    }

    public static void main(String[] args) {
        for (int i = 1; i <= 100; i++) {
            new MyThread("線程"+i).start();
        }
    }
}

在上面的代碼中,線程的run方法在執行耗時的程序之前都需要先獲取testobject對象的鎖,因為對象鎖是公共對象,所以,多個線程同時運行時,同一時刻只能有一個線程獲取鎖,假設某個時刻是 A線程 獲取了鎖,其他線程就會處於等待鎖釋放的阻塞狀態,直到獲取鎖才能繼續執行程序,這就是線程的BLOCKED 狀態。

4、WAITING 表示等待的狀態,處於 WAITING 狀態的線程會進入一個無時間限制的等待,一旦等到了期望的事件,線程就會再次執行,進入RUNNABLE 狀態。最典型的場景就是 等待(wait)通知(notify)

等待狀態對應的方法是wait(),而通知是notify(),這兩個方法並不屬於Thread類,而是屬於Object類,所以所有對象都可以使用這兩個方法。當一個對象實例 obj

調用 wait() 方法後,當前線程就會在這個對象上等待,直到其他線程調用 obj.notify() 為止。這時的對象實例 obj 就相當於多個線程之間的通信工具。實例代碼如下:

public class ThreadDemo {

    public static Object testObject = new Object();

    public static class MyThread1 extends Thread {
        @Override
        public void run() {
            synchronized (testObject) {
                System.out.println("MyThread1 wait :" + System.currentTimeMillis());
                try {
                    //調用wait方法進入等待狀態
                    testObject.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static class MyThread2 extends Thread {
        @Override
        public void run() {

            synchronized (testObject) {
                System.out.println("MyThread2 start notify :" + System.currentTimeMillis());
                //..............耗時操作..............

                //發出通知,喚醒等待的線程
                testObject.notify();
            }
        }
    }

    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1();
        MyThread2 t2 = new MyThread2();
        t1.start();
        t2.start();
    }
}

5、TIMED_WAITINGWAITING 一樣,都表示等待狀態,但TIMED_WAITING 會進行一個 有時限的等待。操作線程狀態有幾個方法是帶有超時參數的,調用方法的線程進入計時等待狀態。這一狀態將一直保持到超時期滿或者接收到適當的通知,最常見的應用就是調用 Thread.sleep() 方法。

實例代碼如下:

public static class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("MyThread start :" + System.currentTimeMillis());
        try {
        //休眠兩秒鐘
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("MyThread end :" + System.currentTimeMillis());
    }
}
public static void main(String[] args) {
        MyThread t = new MyThread1();
        t.start();
 }

啟動線程後,程序運行到 Thread.sleep() 方法會處於休眠狀態,時間根據參數來決定,單位是毫秒,所以執行main方法後,後一條輸出內容會隔兩秒鐘出現。

MyThread start :1544704974271
MyThread end :1544704976272

6、當線程執行完畢後,進入 TERMINATED 狀態,表示結束,一般線程被終止有兩種原因:

  • run方法正常運行後就自然消亡。

  • 因為一個沒有捕獲的異常終止了run方法而導致意外死亡。

好了,線程的生命周期就總結完了,用一張圖表示大概是這樣:
技術分享圖片

Java並發編程:線程的生命周期是個怎樣的過程?