目錄
1.線程的狀態
1.1 NEW
1.2 RUNNABLE
1.3?BLOCKED
1.4 WAITING
1.5 TIMED_WAITING
1.6 TERMINATED
2.線程狀態的相互轉換
在上期的學習中,已經理解線程的啟動(start()
)、休眠(sleep()
)、中斷(interrupt()
)和等待(join()
)等核心概念。通過這些操作,線程會在不同的狀態之間切換。那么,線程在這些過程中究竟經歷了哪些狀態?它們是如何轉換的?這是本期要探討的問題。
在前面談到進程狀態時,主要講了就緒狀態和阻塞狀態,這是兩個經典的狀態。在線程中,線程的狀態有六種,分別是:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。這幾個狀態在 Java 中屬于枚舉類型,可以用代碼實現查看這些狀態:
public class Demo14 {public static void main(String[] args) {for (Thread.State state : Thread.State.values()) {System.out.println(state);}}
}
下面將對它們一 一介紹。
1.線程的狀態
1.1 NEW
小帥對自己的女神小美仰慕已久,小帥同學打算好好打扮自己去追求女神小美,但是小帥心里膽怯,所以到目前為止,只是停留在仰慕的狀態。此時小帥心里有追求女神小美的想法,但還沒開始行動 start(),屬于 NEW 狀態。
NEW:是指一個線程已經被創建(new),但是還沒開始執行的狀態,稱新建狀態,也就是這個時候還沒用調用 start()。
public class Demo14 {public static void main(String[] args) {Thread thread = new Thread(() -> {//.........},"小帥");System.out.println(thread.getName() + ":" + thread.getState());//thread.start();//這里已經注釋,沒有調用start()}
}
運行結果:
1.2 RUNNABLE
終于,小帥下定決心,開啟了對小美的追求之路。每天會和女神聊聊天,偶爾會約女神吃飯,也會給女神送禮物。此時小帥已經有實際行動了 start(),女神小美可能會立即回復,也有可能在忙其他事情,屬于 RUNNABLE 狀態。
RUNNABLE:是指線程正在 JVM 中正在執行或者準備執行的狀態,成可運行狀態。也就是線程已經創建,并且調用了 start() 方法,新建線程開始執行。
public class Demo14 {public static void main(String[] args) {Thread thread = new Thread(() -> {},"小帥");System.out.println("調用start()前:" + thread.getName() + ":" + thread.getState());thread.start();System.out.println("調用start()后:" + thread.getName() + ":" + thread.getState());}
}
運行結果:
1.3?BLOCKED
小帥追求一段時間后,小美也對他有所回應,于是小帥想約小美周末一起看個電影,但是小美說你約晚了,周末要和好閨蜜一起去逛街。此時小帥不能約到小美,因為小美被閨蜜占用了,屬于BLOCKED 狀態。
BLOCKED:是指線程處于阻塞狀態。這個是由于鎖導致的,在下期線程安全問題會有講解,這里先做一個了解。
public class Demo14 {public static void main(String[] args) throws InterruptedException {Object locker = new Object();Thread thread1 = new Thread(() -> {synchronized (locker){while (true) {System.out.println("閨蜜和小美在逛街....");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}},"閨蜜");Thread thread2 = new Thread(() -> {synchronized (locker){System.out.println("小帥約小美看電影....");}},"小帥");thread1.start();thread2.start();}
}
這里需要借助 jconsole 查看狀態,關于 jconsole 在前面的文章《多線程——認識Thread類和創建線程》有講。狀態如下:
可以看到,“小帥”這個線程的狀態就是 BLOCKED。
1.4 WAITING
時間來到第二周,雖然上周小帥沒有約到小美一起看電影,但是啊,小帥沒有放棄,于是小帥再次鼓氣勇氣再約女神一次,這次女神說這周末目前沒有安排,但是這周工作有點忙,具體要看后面的時間安排,于是說“等我有時間聯系你把”,但是小美并沒有說會多久聯系。于是呢,小帥覺得有點希望的,所以就一直等啊等,不分白天黑夜的一直在等女神的消息。此時,小帥一直等,沒有期限的等待就屬于 WAITING 狀態。
WAITING:是指某線程無限期等待其他線程執行特定任務的狀態,稱等待狀態。也就是說需要其他線程完成一定的任務后,這個狀態才能進行,需要調用?join()?方法。
public class Demo14 {public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread(() -> {while (true) {try {System.out.println("等待小美聯系中....");Thread.currentThread().join();} catch (InterruptedException e) {throw new RuntimeException(e);}}},"小帥");Thread thread2 = new Thread(() -> {System.out.println("回復了小帥的邀約....");},"小美");thread1.start();thread1.join();thread2.start();}
}
借助 jconsole 查看狀態,狀態如下:
1.5 TIMED_WAITING
如果上面例子中,如果小美對小帥說“我這周比較忙,不一定有時間,我兩三天內再回復你能不能出去看電影吧。”此時,小帥就會等小美的回復,但不會一直等下去了,最多就等三天,此時的等待就屬于 TIMED_WAITING。
TIMED_WAITING:是指線程在指定的時間內等待,這個等待有期限,稱計時等待狀態。
public class Demo14 {public static void main(String[] args) throws InterruptedException {Object locker = new Object();Thread thread1 = new Thread(() -> {while (true) {System.out.println("等待小美聯系中....");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}},"小帥");Thread thread2 = new Thread(() -> {System.out.println("回復了小帥的邀約....");},"小美");thread1.start();thread1.join(5000);//這里設置等待5秒thread2.start();}
}
借助 jconsole 查看狀態,狀態如下:
1.6 TERMINATED
小帥和小美在聯系一段時間后,小帥和小美決定彼此并不合適,所以并沒有在一起,小帥也放棄了追求小美,至此小帥對仰慕已久的女神的追求徹底結束了,屬于 TERMINATED 狀態。
TERMINATED:是指線程執行完畢的狀態,稱終止狀態。
public class Demo14 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{//....},"小帥");thread.start();thread.join();System.out.println(thread.getName() + "對女神的追求結束了... 現在的狀態是:" + thread.getState());}
}
運行結果:
2.線程狀態的相互轉換
雖然線程狀態的相互轉換看似復雜,但主要是理解各個狀態的含義以及是怎么發生的,這對于在后續的多線程學習中是非常重要的,尤其是調試程序找 bug 的時候,想要在多線程程序中進行調試,理解線程的狀態是非常重要的,并且還需會使用 jconsole 或者其他根據查看當前線程的狀態,比如前文使用 jconsole 查看狀態時,還可以看到一些其他信息比如當前的狀態是發生在哪一行。在這張轉換圖里,看到了一些方法比如 wait(),這個也是等待的方法,用法和 join() 類似,但在這里不深入講解,在后面會專門寫一期 wait() 的用法,以及 synchronized() { } 等都會詳細解析介紹到。
本期主要介紹多線程的線程狀態,主要過程狀態是NEW -> RUNNABLE -> TERMINATED,然后在這個過程中發生一些狀態轉換,主要理解每個狀態的含義并可以借助一些工具如? jconsole 查看線程狀態。
從多線程的創建學習到現在,我們也理解了多線程的優勢,但是那多線程一定很安全嗎?是不是多線程可以隨意使用呢?在 Java 基礎學習中,我們談到?StringBuffer 和 StringBuilder 時,說 StringBuffer 是線程安全的,而 StringBuilder 是線程不安全的。可見線程會存在安全問題,為什么會有線程安全問題以及導致線程安全問題的原因是什么?
欲知后事如何,且聽下回分解!