線程的狀態(tài)詳解
1. 前言
本節(jié)內(nèi)容主要是對(duì)多線程的 6 種狀態(tài)進(jìn)行詳細(xì)講解,具體內(nèi)容點(diǎn)如下:
- 拋開(kāi)語(yǔ)言,談操作系統(tǒng)的線程的生命周期及線程 5 種狀態(tài),這是我們學(xué)習(xí) Java 多線程 6 種狀態(tài)的基礎(chǔ);
- 掌握 Java 的線程生命周期及 6 種線程狀態(tài),是我們本節(jié)課程的重點(diǎn)內(nèi)容;
- 理解 Java 線程 6 種狀態(tài)的定義,并且通過(guò)代碼實(shí)例進(jìn)行實(shí)戰(zhàn)演練,更深入的掌握線程的 6 種不同狀態(tài),是我們本節(jié)內(nèi)容的核心知識(shí);
- 掌握 Java 線程不同狀態(tài)之間的轉(zhuǎn)變關(guān)系,更好地理解線程的不同狀態(tài),是我們本節(jié)課程的重點(diǎn)。
2. 操作系統(tǒng)線程的生命周期
定義:當(dāng)線程被創(chuàng)建并啟動(dòng)以后,它既不是一啟動(dòng)就進(jìn)入了執(zhí)行狀態(tài),也不是一直處于執(zhí)行狀態(tài)。在線程的生命周期中,它要經(jīng)過(guò)新建 (New)、就緒(Runnable)、運(yùn)行(Running)、阻塞 (Blocked),和死亡 (Dead) 5 種狀態(tài)。
從線程的新建 (New) 到死亡 (Dead),就是線程的整個(gè)生命周期。
下面我們分別對(duì) 5 種不同的狀態(tài)進(jìn)行概念解析。
新建 (New):操作系統(tǒng)在進(jìn)程中新建一條線程,此時(shí)線程是初始化狀態(tài)。
就緒 (Runnable):就緒狀態(tài),可以理解為隨時(shí)待命狀態(tài),一切已準(zhǔn)備就緒,隨時(shí)等待運(yùn)行命令。
運(yùn)行 (Running):CPU 進(jìn)行核心調(diào)度,對(duì)已就緒狀態(tài)的線程進(jìn)行任務(wù)分配,接到調(diào)度命令,進(jìn)入線程運(yùn)行狀態(tài)。
阻塞 (Blocked):線程鎖導(dǎo)致的線程阻塞狀態(tài)。共享內(nèi)存區(qū)域的共享文件,當(dāng)有兩個(gè)或兩個(gè)以上的線程進(jìn)行非讀操作時(shí),只允許一個(gè)線程進(jìn)行操作,其他線程在第一個(gè)線程未釋放鎖之前不可進(jìn)入操作,此時(shí)進(jìn)入的一個(gè)線程是運(yùn)行狀態(tài),其他線程為阻塞狀態(tài)。
死亡 (Dead):線程工作結(jié)束,被操作系統(tǒng)回收。
3. Java 的線程的生命周期及狀態(tài)
定義: 在 Java 線程的生命周期中,它要經(jīng)過(guò)新建(New),運(yùn)行(Running),阻塞(Blocked),等待(Waiting),超時(shí)等待(Timed_Waiting)和終止?fàn)顟B(tài)(Terminal)6 種狀態(tài)。
從線程的新建(New)到終止?fàn)顟B(tài)(Terminal),就是線程的整個(gè)生命周期。
Tips :與操作系統(tǒng)相比, Java 線程是否少了 “就緒” 狀態(tài) ?其實(shí) Java 線程依然有就緒狀態(tài),只不過(guò) Java 線程將 “就緒(Runnable)" 和 “運(yùn)行(Running)” 兩種狀態(tài)統(tǒng)一歸結(jié)為 “運(yùn)行(Running)” 狀態(tài)。
我們來(lái)看下 Java 線程的 6 種狀態(tài)的概念。
新建 (New):實(shí)現(xiàn) Runnable 接口或者繼承 Thead 類(lèi)可以得到一個(gè)線程類(lèi),new 一個(gè)實(shí)例出來(lái),線程就進(jìn)入了初始狀態(tài)。
運(yùn)行 (Running):線程調(diào)度程序從可運(yùn)行池中選擇一個(gè)線程作為當(dāng)前線程時(shí)線程所處的狀態(tài)。這也是線程進(jìn)入運(yùn)行狀態(tài)的唯一方式。
阻塞 (Blocked):阻塞狀態(tài)是線程在進(jìn)入 synchronized 關(guān)鍵字修飾的方法或者代碼塊時(shí),由于其他線程正在執(zhí)行,不能夠進(jìn)入方法或者代碼塊而被阻塞的一種狀態(tài)。
等待 (Waiting):執(zhí)行 wait () 方法后線程進(jìn)入等待狀態(tài),如果沒(méi)有顯示的 notify () 方法或者 notifyAll () 方法喚醒,該線程會(huì)一直處于等待狀態(tài)。
超時(shí)等待 (Timed_Waiting):執(zhí)行 sleep(Long time)方法后,線程進(jìn)入超時(shí)等待狀態(tài),時(shí)間一到,自動(dòng)喚醒線程。
終止?fàn)顟B(tài) (Terminal):當(dāng)線程的 run () 方法完成時(shí),或者主線程的 main () 方法完成時(shí),我們就認(rèn)為它終止了。這個(gè)線程對(duì)象也許是活的,但是,它已經(jīng)不是一個(gè)單獨(dú)執(zhí)行的線程。線程一旦終止了,就不能復(fù)生。
4. 新建(New)狀態(tài)詳解
實(shí)例:
public class ThreadTest implements Runnable{
@Override
public void run() {
System.out.println("線程:"+Thread.currentThread()+" 正在執(zhí)行...");
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new ThreadTest()); //線程 創(chuàng)建(NEW)狀態(tài)
}
}
這里僅僅對(duì)線程進(jìn)行了創(chuàng)建,沒(méi)有執(zhí)行其他方法。 此時(shí)線程的狀態(tài)就是新建 (New) 狀態(tài)。
Tips:新建(New)狀態(tài)的線程,是沒(méi)有執(zhí)行 start () 方法的線程。
5. 運(yùn)行(Running)狀態(tài)詳解
定義: 線程調(diào)度程序從可運(yùn)行池中選擇一個(gè)線程作為當(dāng)前線程時(shí)線程所處的狀態(tài)。這也是線程進(jìn)入運(yùn)行狀態(tài)的唯一方式。
public class ThreadTest implements Runnable{
.......
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new ThreadTest()); //線程 創(chuàng)建(NEW)狀態(tài)
t1. start(); //線程進(jìn)入 運(yùn)行(Running)狀態(tài)
}
}
當(dāng)線程調(diào)用 start () 方法后,線程才進(jìn)入了運(yùn)行(Running)狀態(tài)。
6. 阻塞(Blocked)狀態(tài)詳解
定義: 阻塞狀態(tài)是線程阻塞在進(jìn)入 synchronized 關(guān)鍵字修飾的方法或者代碼塊時(shí)的狀態(tài)。
我們先來(lái)分析如下代碼。
實(shí)例:
public class DemoTest implements Runnable{
@Override
public void run() {
testBolockStatus();
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new DemoTest()); //線程 t1創(chuàng)建(NEW)狀態(tài)
t1.setName("T-one");
Thread t2 = new Thread(new DemoTest()); //線程 t2創(chuàng)建(NEW)狀態(tài)
t2.setName("T-two");
t1. start(); //線程 t1 進(jìn)入 運(yùn)行(Running)狀態(tài)
t2. start(); //線程 t2 進(jìn)入 運(yùn)行(Running)狀態(tài)
}
public static synchronized void testBolockStatus(){ // 該方法被 synchronized修飾
System.out.println("我是被 synchronized 修飾的同步方法, 正在有線程" +
Thread.currentThread().getName() +
"執(zhí)行我,其他線程進(jìn)入阻塞狀態(tài)排隊(duì)。");
}
}
代碼分析:
首先,請(qǐng)看關(guān)鍵代碼:
t1. start(); //線程 t1 進(jìn)入 運(yùn)行(Running)狀態(tài)
t2. start(); //線程 t2 進(jìn)入 運(yùn)行(Running)狀態(tài)
我們將線程 t1 和 t2 進(jìn)行 運(yùn)行狀態(tài)的啟動(dòng),此時(shí) t1 和 t2 就會(huì)執(zhí)行 run () 方法下的 sync testBolockStatus () 方法。
然后,請(qǐng)看關(guān)鍵代碼:
public static synchronized void testBolockStatus(){ // 該方法被 synchronized修飾
testBolockStatus () 方法是被 synchronized 修飾的同步方法。當(dāng)有 2 條或者 2 條以上的線程執(zhí)行該方法時(shí), 除了進(jìn)入方法的一條線程外,其他線程均處于 “阻塞” 狀態(tài)。
最后,我們看下執(zhí)行結(jié)果:
我是被 synchronized 修飾的同步方法, 正在有線程T-one執(zhí)行我,其他線程進(jìn)入阻塞狀態(tài)排隊(duì)。
我是被 synchronized 修飾的同步方法, 正在有線程T-two執(zhí)行我,其他線程進(jìn)入阻塞狀態(tài)排隊(duì)。
執(zhí)行結(jié)果解析:我們有兩條線程, 線程名稱分別為: T-one 和 T-two。
- 執(zhí)行結(jié)果第一條: T-one 的狀態(tài)當(dāng)時(shí)為 運(yùn)行(Running)狀態(tài),T-two 狀態(tài)為 阻塞(Blocked)狀態(tài);
- 執(zhí)行結(jié)果第二條: T-two 的狀態(tài)當(dāng)時(shí)為 運(yùn)行(Running)狀態(tài),T-one 狀態(tài)為 阻塞(Blocked)狀態(tài)。
7. 等待(Waiting)狀態(tài)詳解
定義: 執(zhí)行 wait () 方法后線程進(jìn)入等待狀態(tài),如果沒(méi)有顯示的 notify () 方法或者 notifyAll () 方法喚醒,該線程會(huì)一直處于等待狀態(tài)。
我們通過(guò)代碼來(lái)看下,等待(Waiting)狀態(tài)。
實(shí)例:
public class DemoTest implements Runnable{
@Override
public void run() {
try {
testBolockStatus();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new DemoTest()); //線程 t1創(chuàng)建(NEW)狀態(tài)
t1.setName("T-one");
t1. start(); //線程進(jìn)入 運(yùn)行 狀態(tài)
}
public synchronized void testBolockStatus() throws InterruptedException {
System.out.println("我是線程:" + Thread.currentThread().getName() + ". 我進(jìn)來(lái)了。");
this.wait(); //線程進(jìn)入 等待狀態(tài) ,沒(méi)有其他線程 喚醒, 會(huì)一直等待下去
System.out.println("我是被 synchronized 修飾的同步方法, 正在有線程" +
Thread.currentThread().getName() +
"執(zhí)行我,其他線程進(jìn)入阻塞狀態(tài)排隊(duì)。");
}
}
注意看下關(guān)鍵代碼:
this.wait(); //線程進(jìn)入 等待狀態(tài) ,沒(méi)有其他線程 喚醒, 會(huì)一直等待下去
這里調(diào)用了 wait () 方法。線程進(jìn)入 等待(Waiting)狀態(tài)。如果沒(méi)有其他線程喚醒,會(huì)一直維持等待狀態(tài)。
運(yùn)行結(jié)果:
我是線程:T-one. 我進(jìn)來(lái)了。
沒(méi)有辦法打印 wait () 方法后邊的執(zhí)行語(yǔ)句,因?yàn)榫€程已經(jīng)進(jìn)入了等待狀態(tài)。
8. 超時(shí)等待(Timed-Waiting)狀態(tài)詳解
定義: 執(zhí)行 sleep(Long time)方法后,線程進(jìn)入超時(shí)等待狀態(tài),時(shí)間一到,自動(dòng)喚醒線程。
我們通過(guò)代碼來(lái)看下,超時(shí)等待(Timed-Waiting)狀態(tài)。
實(shí)例:
public class DemoTest implements Runnable{
@Override
public void run() {
.....
}
public static void main(String[] args) throws InterruptedException {
.....
}
public synchronized void testBolockStatus() throws InterruptedException {
System.out.println("我是線程:" + Thread.currentThread().getName() + ". 我進(jìn)來(lái)了。");
Thread.sleep(5000); //超時(shí)等待 狀態(tài) 5 秒后自動(dòng)喚醒線程。
System.out.println("我是被 synchronized 修飾的同步方法, 正在有線程" +
Thread.currentThread().getName() +
"執(zhí)行我,其他線程進(jìn)入阻塞狀態(tài)排隊(duì)。");
}
}
注意看下關(guān)鍵代碼:
Thread.sleep(5000); //超時(shí)等待 狀態(tài) 5 秒后自動(dòng)喚醒線程。
這里調(diào)用了 sleep () 方法。線程進(jìn)入超時(shí)等待(Timed-Waiting)狀態(tài)。超時(shí)等待時(shí)間結(jié)束,自動(dòng)喚醒線程繼續(xù)執(zhí)行。
運(yùn)行結(jié)果:5 秒后,打印第二條語(yǔ)句。
我是線程:T-one. 我進(jìn)來(lái)了。
我睡醒了。我是被 synchronized 修飾的同步方法, 正在有線程T-one執(zhí)行我,其他線程進(jìn)入阻塞狀態(tài)排隊(duì)。
9. 終止(Terminal)狀態(tài)定義
定義: 當(dāng)線程的 run () 方法完成時(shí),或者主線程的 main () 方法完成時(shí),我們就認(rèn)為它終止了。這個(gè)線程對(duì)象也許是活的,但是,它已經(jīng)不是一個(gè)單獨(dú)執(zhí)行的線程。線程一旦終止了,就不能復(fù)生。
10. 小結(jié)
本節(jié)的重中之重在于線程的 6 種不同的狀態(tài),本節(jié)所有的內(nèi)容都圍繞這 6 種不同的狀態(tài)進(jìn)行的講解,這也是本小節(jié)的核心內(nèi)容,也是必須要掌握的內(nèi)容。