天天看點

JDK1.9- 線程狀态

3.1 線程狀态概述

當線程被建立并啟動以後,它既不是一啟動就進入了執行狀态,也不是一直處于執行狀态。線上程的生命周期中, 有幾種狀态呢?在API中 java.lang.Thread.State 這個枚舉中給出了六種線程狀态:

這裡先列出各個線程狀态發生的條件,下面将會對每種狀态進行詳細解析

JDK1.9- 線程狀态

我們不需要去研究這幾種狀态的實作原理,我們隻需知道在做線程操作中存在這樣的狀态。那我們怎麼去了解這幾 個狀态呢,建立與被終止還是很容易了解的,我們就研究一下線程從Runnable(可運作)狀态與非運作狀态之間 的轉換問題。

3.2 Timed Waiting(計時等待)

Timed Waiting在API中的描述為:一個正在限時等待另一個線程執行一個(喚醒)動作的線程處于這一狀态。單獨 的去了解這句話,真是玄之又玄,其實我們在之前的操作中已經接觸過這個狀态了,在哪裡呢?

在我們寫賣票的案例中,為了減少線程執行太快,現象不明顯等問題,我們在run方法中添加了sleep語句,這樣就 強制目前正在執行的線程休眠(暫停執行),以“減慢線程”。

其實當我們調用了sleep方法之後,目前執行的線程就進入到“休眠狀态”,其實就是所謂的Timed Waiting(計時等 待),那麼我們通過一個案例加深對該狀态的一個了解。

實作一個計數器,計數到100,在每個數字之間暫停1秒,每隔10個數字輸出一個字元串

代碼:

public class MyThread extends Thread {
    public void run() {
        for (int i = 0; i < 100; i++) {
            if ((i) % 10 == 0) {
                System.out.println("‐‐‐‐‐‐‐" + i);
            }
            System.out.print(i);
            try {
                Thread.sleep(1000);
                System.out.print(" 線程睡眠1秒!\n");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        new MyThread().start();
    }
}
           

通過案例可以發現,sleep方法的使用還是很簡單的。我們需要記住下面幾點:

  1. 進入 TIMED_WAITING 狀态的一種常見情形是調用的 sleep 方法,單獨的線程也可以調用,不一定非要有協 作關系。
  2. 為了讓其他線程有機會執行,可以将Thread.sleep()的調用放線程run()之内。這樣才能保證該線程執行過程 中會睡眠
  3. sleep與鎖無關,線程睡眠到期自動蘇醒,并傳回到Runnable(可運作)狀态。
小提示:sleep()中指定的時間是線程不會運作的最短時間。是以,sleep()方法不能保證該線程睡眠到期後就 開始立刻執行。

Timed Waiting 線程狀态圖:

JDK1.9- 線程狀态
3.3 BLOCKED(鎖阻塞)

Blocked狀态在API中的介紹為:一個正在阻塞等待一個螢幕鎖(鎖對象)的線程處于這一狀态。

我們已經學完同步機制,那麼這個狀态是非常好了解的了。比如,線程A與線程B代碼中使用同一鎖,如果線程A獲 取到鎖,線程A進入到Runnable狀态,那麼線程B就進入到Blocked鎖阻塞狀态。

這是由Runnable狀态進入Blocked狀态。除此Waiting以及Time Waiting狀态也會在某種情況下進入阻塞狀态,而 這部分内容作為擴充知識點帶領大家了解一下。

Blocked 線程狀态圖

JDK1.9- 線程狀态
3.4 Waiting(無限等待)

Wating狀态在API中介紹為:一個正在無限期等待另一個線程執行一個特别的(喚醒)動作的線程處于這一狀态。

那麼我們之前遇到過這種狀态嗎?答案是并沒有,但并不妨礙我們進行一個簡單深入的了解。我們通過一段代碼來 學習一下:

public class WaitingTest {
    public static Object obj = new Object();

    public static void main(String[] args) {
        // 示範waiting
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    synchronized (obj) {
                        try {
                            System.out.println(Thread.currentThread().getName() + "=== 擷取到鎖對象,調用wait方法,進入waiting狀态,釋放鎖對象");
                            obj.wait();
                            //無限等待
                            obj.wait(5000);
                            //計時等待, 5秒 時間到,自動醒來
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "=== 從waiting狀 态醒來,擷取到鎖對象,繼續執行了");
                    }
                }
            }
        }, "等待線程").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    //每隔3秒 喚醒一次
                    try {
                        System.out.println(Thread.currentThread().getName() + "‐‐‐‐‐ 等待3秒鐘");
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (obj) {
                        System.out.println(Thread.currentThread().getName() + "‐‐‐‐‐ 擷取到鎖對 象,調用notify方法,釋放鎖對象");
                        obj.notify();
                    }
                }
            }
        }, "喚醒線程").start();
    }
}
           

通過上述案例我們會發現,一個調用了某個對象的 Object.wait 方法的線程會等待另一個線程調用此對象的 Object.notify()方法 或 Object.notifyAll()方法。

其實waiting狀态并不是一個線程的操作,它展現的是多個線程間的通信,可以了解為多個線程之間的協作關系, 多個線程會争取鎖,同時互相之間又存在協作關系。就好比在公司裡你和你的同僚們,你們可能存在晉升時的競 争,但更多時候你們更多是一起合作以完成某些任務。

當多個線程協作時,比如A,B線程,如果A線程在Runnable(可運作)狀态中調用了wait()方法那麼A線程就進入 了Waiting(無限等待)狀态,同時失去了同步鎖。假如這個時候B線程擷取到了同步鎖,在運作狀态中調用了 notify()方法,那麼就會将無限等待的A線程喚醒。注意是喚醒,如果擷取到鎖對象,那麼A線程喚醒後就進入 Runnable(可運作)狀态;如果沒有擷取鎖對象,那麼就進入到Blocked(鎖阻塞狀态)。

3.5 補充知識點