Java并發(fā)編程:線程的生命周期是個(gè)怎樣的過程?

前言

在日常開發(fā)過程中,如果我們需要執(zhí)行一些比較耗時(shí)的程序的話,一般來說都是開啟一個(gè)新線程,把耗時(shí)的代碼放在線程里,然后開啟線程執(zhí)行。但線程是會(huì)耗費(fèi)系統(tǒng)資源的,如果有多個(gè)線程同時(shí)運(yùn)行,互相之間搶占系統(tǒng)資源,那無疑會(huì)對(duì)系統(tǒng)造成極大的壓力。所以,怎么操作線程,保證不影響整個(gè)應(yīng)用功能是很重要的,而這就需要我們了解線程的生命周期了。

線程的生命周期

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

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

    private State() {
    }
}

1、NEW 狀態(tài)表示剛剛創(chuàng)建的線程,此時(shí)的線程還沒運(yùn)行,也就是還沒執(zhí)行start() 方法,創(chuàng)建線程的方式也比較簡(jiǎn)單,可以參考《Java并發(fā)編程:Java創(chuàng)建線程的三種方式》。

2、當(dāng)線程執(zhí)行時(shí),處于 RUNNABLE 狀態(tài),表示線程所需的資源已經(jīng)準(zhǔn)備好了。

3、如果線程在執(zhí)行的過程中遇到被阻塞的情況,例如線程中的程序中有synchronized 同步代碼塊,線程就會(huì)暫停執(zhí)行,進(jìn)入阻塞狀態(tài),直至獲取請(qǐng)求的鎖,這時(shí)線程就處于 BLOCKED 狀態(tài)。

實(shí)例代碼如下:

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對(duì)象
            synchronized (testObject) {
                System.out.println("thread name:" + this.getName());

                //..............耗時(shí)操作..............
            }
        }

    }

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

在上面的代碼中,線程的run方法在執(zhí)行耗時(shí)的程序之前都需要先獲取testobject對(duì)象的鎖,因?yàn)閷?duì)象鎖是公共對(duì)象,所以,多個(gè)線程同時(shí)運(yùn)行時(shí),同一時(shí)刻只能有一個(gè)線程獲取鎖,假設(shè)某個(gè)時(shí)刻是 A線程 獲取了鎖,其他線程就會(huì)處于等待鎖釋放的阻塞狀態(tài),直到獲取鎖才能繼續(xù)執(zhí)行程序,這就是線程的BLOCKED 狀態(tài)。

4、WAITING 表示等待的狀態(tài),處于 WAITING 狀態(tài)的線程會(huì)進(jìn)入一個(gè)無時(shí)間限制的等待,一旦等到了期望的事件,線程就會(huì)再次執(zhí)行,進(jìn)入RUNNABLE 狀態(tài)。最典型的場(chǎng)景就是 等待(wait)通知(notify)

等待狀態(tài)對(duì)應(yīng)的方法是wait(),而通知是notify(),這兩個(gè)方法并不屬于Thread類,而是屬于Object類,所以所有對(duì)象都可以使用這兩個(gè)方法。當(dāng)一個(gè)對(duì)象實(shí)例 obj 調(diào)用 wait() 方法后,當(dāng)前線程就會(huì)在這個(gè)對(duì)象上等待,直到其他線程調(diào)用 obj.notify() 為止。這時(shí)的對(duì)象實(shí)例 obj 就相當(dāng)于多個(gè)線程之間的通信工具。實(shí)例代碼如下:

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 {
                    //調(diào)用wait方法進(jìn)入等待狀態(tài)
                    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());
                //..............耗時(shí)操作..............

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

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

5、TIMED_WAITINGWAITING 一樣,都表示等待狀態(tài),但TIMED_WAITING 會(huì)進(jìn)行一個(gè) 有時(shí)限的等待。操作線程狀態(tài)有幾個(gè)方法是帶有超時(shí)參數(shù)的,調(diào)用方法的線程進(jìn)入計(jì)時(shí)等待狀態(tài)。這一狀態(tài)將一直保持到超時(shí)期滿或者接收到適當(dāng)?shù)耐ㄖ?,最常見的?yīng)用就是調(diào)用 Thread.sleep() 方法。

實(shí)例代碼如下:

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();
 }

啟動(dòng)線程后,程序運(yùn)行到 Thread.sleep() 方法會(huì)處于休眠狀態(tài),時(shí)間根據(jù)參數(shù)來決定,單位是毫秒,所以執(zhí)行main方法后,后一條輸出內(nèi)容會(huì)隔兩秒鐘出現(xiàn)。

MyThread start :1544704974271
MyThread end :1544704976272

6、當(dāng)線程執(zhí)行完畢后,進(jìn)入 TERMINATED 狀態(tài),表示結(jié)束,一般線程被終止有兩種原因:

  • run方法正常運(yùn)行后就自然消亡。

  • 因?yàn)橐粋€(gè)沒有捕獲的異常終止了run方法而導(dǎo)致意外死亡。

好了,線程的生命周期就總結(jié)完了,用一張圖表示大概是這樣:


生命周期.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 0 前言 當(dāng)線程被創(chuàng)建并啟動(dòng)以后,它既不是一啟動(dòng)就進(jìn)入了執(zhí)行狀態(tài),也不是一直處于執(zhí)行狀態(tài)。在線程的生命周期中,它要...
    七寸知架構(gòu)閱讀 5,212評(píng)論 2 63
  • 進(jìn)程和線程 進(jìn)程 所有運(yùn)行中的任務(wù)通常對(duì)應(yīng)一個(gè)進(jìn)程,當(dāng)一個(gè)程序進(jìn)入內(nèi)存運(yùn)行時(shí),即變成一個(gè)進(jìn)程.進(jìn)程是處于運(yùn)行過程中...
    勝浩_ae28閱讀 5,142評(píng)論 0 23
  • 3. 創(chuàng)建新線程 創(chuàng)建一個(gè)線程有兩種方法: 從Thread類繼承并重寫run()方法。創(chuàng)建一個(gè)實(shí)例,調(diào)用star...
    vancent閱讀 500評(píng)論 0 0
  • 1. cpu通過時(shí)間片分配算法來循環(huán)執(zhí)行任務(wù),當(dāng)前任務(wù)執(zhí)行一個(gè)時(shí)間片后會(huì)切換到下一任務(wù)。但是,再切換之前會(huì)保存上一...
    冰與河豚魚閱讀 681評(píng)論 0 0
  • 第三章 Java內(nèi)存模型 3.1 Java內(nèi)存模型的基礎(chǔ) 通信在共享內(nèi)存的模型里,通過寫-讀內(nèi)存中的公共狀態(tài)進(jìn)行隱...
    澤毛閱讀 4,382評(píng)論 2 22