您好,登錄后才能下訂單哦!
Java中,線(xiàn)程的狀態(tài)使用一個(gè)枚舉類(lèi)型來(lái)描述的。這個(gè)枚舉一共有6個(gè)值: NEW(新建)、RUNNABLE(運(yùn)行)、BLOCKED(鎖池)、TIMED_WAITING(定時(shí)等待)、WAITING(等待)、TERMINATED(終止、結(jié)束)。
但是大多數(shù)人的理解和上面的這六種還是有些差別,通常會(huì)加上阻塞狀態(tài),可運(yùn)行狀態(tài),掛起狀態(tài)。
在API中 java.lang.Thread.State
這個(gè)枚舉中給出了六種線(xiàn)程狀態(tài),
這是Thread類(lèi)描述線(xiàn)程狀態(tài)的枚舉類(lèi)的源代碼:
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
線(xiàn)程從創(chuàng)建、運(yùn)行到結(jié)束總是處于下面五個(gè)狀態(tài)之一:新建狀態(tài)、就緒狀態(tài)、運(yùn)行狀態(tài)、阻塞狀態(tài)及死亡狀態(tài)。
線(xiàn)程狀態(tài)轉(zhuǎn)換圖:
當(dāng)用new操作符創(chuàng)建一個(gè)線(xiàn)程時(shí), 例如new Thread(r)
,線(xiàn)程還沒(méi)有開(kāi)始運(yùn)行,此時(shí)線(xiàn)程處在新建狀態(tài)。 當(dāng)一個(gè)線(xiàn)程處于新生狀態(tài)時(shí),程序還沒(méi)有開(kāi)始運(yùn)行線(xiàn)程中的代碼。
一個(gè)新創(chuàng)建的線(xiàn)程并不自動(dòng)開(kāi)始運(yùn)行,要執(zhí)行線(xiàn)程,必須調(diào)用線(xiàn)程的start()方法。當(dāng)線(xiàn)程對(duì)象調(diào)用start()方法即啟動(dòng)了線(xiàn)程,start()方法創(chuàng)建線(xiàn)程運(yùn)行的系統(tǒng)資源,并調(diào)度線(xiàn)程運(yùn)行run()方法。當(dāng)start()方法返回后,線(xiàn)程就處于就緒狀態(tài)。
處于就緒狀態(tài)的線(xiàn)程并不一定立即運(yùn)行run()方法,線(xiàn)程還必須同其他線(xiàn)程競(jìng)爭(zhēng)CPU時(shí)間,只有獲得CPU時(shí)間才可以運(yùn)行線(xiàn)程。因?yàn)樵趩蜟PU的計(jì)算機(jī)系統(tǒng)中,不可能同時(shí)運(yùn)行多個(gè)線(xiàn)程,一個(gè)時(shí)刻僅有一個(gè)線(xiàn)程處于運(yùn)行狀態(tài)。因此此時(shí)可能有多個(gè)線(xiàn)程處于就緒狀態(tài)。對(duì)多個(gè)處于就緒狀態(tài)的線(xiàn)程是由
Java運(yùn)行時(shí)系統(tǒng)的線(xiàn)程調(diào)度程序(thread scheduler)來(lái)調(diào)度的。
當(dāng)線(xiàn)程獲得CPU時(shí)間后,它才進(jìn)入運(yùn)行狀態(tài),真正開(kāi)始執(zhí)行run()方法.
線(xiàn)程運(yùn)行過(guò)程中,可能由于各種原因進(jìn)入阻塞狀態(tài):
有兩個(gè)原因會(huì)導(dǎo)致線(xiàn)程死亡:
1.run方法正常退出而自然死亡,
據(jù)官方源碼,一個(gè)線(xiàn)程有六個(gè)狀態(tài),沒(méi)有阻塞狀態(tài),沒(méi)有可運(yùn)行,沒(méi)有掛起狀態(tài)。這里先列出各個(gè)線(xiàn)程狀態(tài)發(fā)生的條件,下面將會(huì)對(duì)每種狀態(tài)進(jìn)行詳細(xì)解析:
線(xiàn)程狀態(tài) | 導(dǎo)致?tīng)顟B(tài)發(fā)生修改的條件 |
---|---|
NEW(新建) | 線(xiàn)程剛被創(chuàng)建,但是并未啟動(dòng)。還沒(méi)調(diào)用start()方法。 |
Runnable(可運(yùn)行) | 線(xiàn)程可以在java虛擬機(jī)中運(yùn)行的狀態(tài),可能正在運(yùn)行自己代碼,也可能沒(méi)有,這取決于操作系統(tǒng)處理器。 |
Blocked(鎖阻塞) | 當(dāng)一個(gè)線(xiàn)程試圖獲取一個(gè)對(duì)象鎖,而該對(duì)象鎖被其他的線(xiàn)程持有,則該線(xiàn)程進(jìn)入Blocked狀態(tài);當(dāng)該線(xiàn)程持有鎖時(shí),該線(xiàn)程將變成Runnable狀態(tài)。 |
Waiting(無(wú)限 等待) | 一個(gè)線(xiàn)程在等待另一個(gè)線(xiàn)程執(zhí)行一個(gè)(喚醒)動(dòng)作時(shí),該線(xiàn)程進(jìn)入Waiting狀態(tài)。進(jìn)入這個(gè)狀態(tài)后是不能自動(dòng)喚醒的,必須等待另一個(gè)線(xiàn)程調(diào)用notify或者notifyAll方法才能夠喚醒。 |
Timed Waiting(計(jì)時(shí)等待) | 同waiting狀態(tài),有幾個(gè)方法有超時(shí)參數(shù),調(diào)用他們將進(jìn)入TimedWaiting狀態(tài)。這一狀態(tài)將一直保持到超時(shí)期滿(mǎn)或者接收到喚醒通知。帶有超時(shí)參數(shù)的常用方法有Thread.sleep、Object.wait。 |
Teminated(被 終止) | 因?yàn)閞un方法正常退出而死亡,或者因?yàn)闆](méi)有捕獲的異常終止了run方法而死亡。 |
我們不需要去研究這幾種狀態(tài)的實(shí)現(xiàn)原理,我們只需知道在做線(xiàn)程操作中存在這樣的狀態(tài)。那我們?cè)趺慈ダ斫膺@幾個(gè)狀態(tài)呢,新建與被終止還是很容易理解的,我們就研究一下線(xiàn)程從Runnable(可運(yùn)行)狀態(tài)與非運(yùn)行狀態(tài)之間 的轉(zhuǎn)換問(wèn)題。
當(dāng)線(xiàn)程調(diào)用sleep()方法或當(dāng)前線(xiàn)程中有其他線(xiàn)程調(diào)用了帶時(shí)間參數(shù)的join()方法的時(shí)候進(jìn)入了定時(shí)等待狀態(tài)(TIMED_WAITING)。
代碼示例:
/**
* @author bruceliu
* @create 2019-06-01 22:35
* @description TimedWaiting(計(jì)時(shí)等待)
*/
public class Test1 {
public static void main(String[] args) {
Thread1 t1 = new Thread1();
Thread2 t2 = new Thread2();
t1.setThread2(t2);
t1.start();
t2.start();
}
}
//Thread1負(fù)責(zé)打印兩個(gè)線(xiàn)程的狀態(tài)。
class Thread1 extends Thread {
private Thread2 t2;
public void setThread2(Thread2 t2) {
this.t2 = t2;
}
@Override
public void run() {
System.out.println("進(jìn)入t1線(xiàn)程");
for (int i = 0; i < 5; i++) {
try {
System.out.println("t1 的狀態(tài): " + getState());
System.out.println("t2 的狀態(tài): " + t2.getState());
System.out.println();
//為了減少打印次數(shù),所以t1每打印一次睡1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
}
class Thread2 extends Thread {
@Override
public void run() {
System.out.println("進(jìn)入t2線(xiàn)程,馬上進(jìn)入睡眠");
try {
//睡眠5秒鐘。
sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t2睡眠結(jié)束");
}
}
通過(guò)案例可以發(fā)現(xiàn),sleep方法的使用還是很簡(jiǎn)單的。我們需要記住下面幾點(diǎn):
sleep()中指定的時(shí)間是線(xiàn)程不會(huì)運(yùn)行的最短時(shí)間。因此,sleep()方法不能保證該線(xiàn)程睡眠到期后就開(kāi)始立刻執(zhí)行。
Timed Waiting 線(xiàn)程狀態(tài)圖:
Blocked狀態(tài)在API中的介紹為:一個(gè)正在阻塞等待一個(gè)監(jiān)視器鎖(鎖對(duì)象)的線(xiàn)程處于這一狀態(tài)。
如果我們已經(jīng)學(xué)完同步機(jī)制,那么這個(gè)狀態(tài)是非常好理解的了。比如,線(xiàn)程A與線(xiàn)程B代碼中使用同一鎖,如果線(xiàn)程A獲取到鎖,線(xiàn)程A進(jìn)入到Runnable狀態(tài),那么線(xiàn)程B就進(jìn)入到Blocked鎖阻塞狀態(tài)。
這是由Runnable狀態(tài)進(jìn)入Blocked狀態(tài)。除此Waiting以及Time Waiting狀態(tài)也會(huì)在某種情況下進(jìn)入阻塞狀態(tài),而這部分內(nèi)容作為擴(kuò)充知識(shí)點(diǎn)帶領(lǐng)大家了解一下。
Blocked 線(xiàn)程狀態(tài)圖:
Wating狀態(tài)在API中介紹為:一個(gè)正在無(wú)限期等待另一個(gè)線(xiàn)程執(zhí)行一個(gè)特別的(喚醒)動(dòng)作的線(xiàn)程處于這一狀態(tài)。那么我們之前遇到過(guò)這種狀態(tài)嗎?答案是并沒(méi)有,但并不妨礙我們進(jìn)行一個(gè)簡(jiǎn)單深入的了解。我們通過(guò)一段代碼來(lái)學(xué)習(xí)一下:
package com.bruceliu.demo7;
/**
* @author bruceliu
* @create 2019-05-30 16:59
* @description
*/
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() + "=== 從waiting狀態(tài)醒來(lái),獲取到鎖對(duì)象,繼續(xù)執(zhí)行了");
obj.wait(); //無(wú)限等待
//obj.wait(5000); //計(jì)時(shí)等待, 5秒 時(shí)間到,自動(dòng)醒來(lái)
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "=== 從waiting狀 態(tài)醒來(lái),獲取到鎖對(duì)象,繼續(xù)執(zhí)行了");
}
}
}
}, "等待線(xiàn)程").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() + "‐‐‐‐‐ 獲取到鎖對(duì) 象,調(diào)用notify方法,釋放鎖對(duì)象");
obj.notify();
}
}
}
}, "喚醒線(xiàn)程").start();
}
}
通過(guò)上述案例我們會(huì)發(fā)現(xiàn),一個(gè)調(diào)用了某個(gè)對(duì)象的 Object.wait 方法的線(xiàn)程會(huì)等待另一個(gè)線(xiàn)程調(diào)用此對(duì)象的Object.notify()方法 或 Object.notifyAll()方法。其實(shí)waiting狀態(tài)并不是一個(gè)線(xiàn)程的操作,它體現(xiàn)的是多個(gè)線(xiàn)程間的通信,可以理解為多個(gè)線(xiàn)程之間的協(xié)作關(guān)系,多個(gè)線(xiàn)程會(huì)爭(zhēng)取鎖,同時(shí)相互之間又存在協(xié)作關(guān)系。就好比在公司里你和你的同事們,你們可能存在晉升時(shí)的競(jìng)爭(zhēng),但更多時(shí)候你們更多是一起合作以完成某些任務(wù)。當(dāng)多個(gè)線(xiàn)程協(xié)作時(shí),比如A,B線(xiàn)程,如果A線(xiàn)程在Runnable(可運(yùn)行)狀態(tài)中調(diào)用了wait()方法那么A線(xiàn)程就進(jìn)入了Waiting(無(wú)限等待)狀態(tài),同時(shí)失去了同步鎖。假如這個(gè)時(shí)候B線(xiàn)程獲取到了同步鎖,在運(yùn)行狀態(tài)中調(diào)用了notify()方法,那么就會(huì)將無(wú)限等待的A線(xiàn)程喚醒。注意是喚醒,如果獲取到鎖對(duì)象,那么A線(xiàn)程喚醒后就進(jìn)入Runnable(可運(yùn)行)狀態(tài);如果沒(méi)有獲取鎖對(duì)象,那么就進(jìn)入到Blocked(鎖阻塞狀態(tài))。
Waiting 線(xiàn)程狀態(tài)圖:
到此為止我們已經(jīng)對(duì)線(xiàn)程狀態(tài)有了基本的認(rèn)識(shí),想要有更多的了解,詳情可以見(jiàn)下圖:
我們?cè)诜咥PI的時(shí)候會(huì)發(fā)現(xiàn)Timed Waiting(計(jì)時(shí)等待) 與 Waiting(無(wú)限等待) 狀態(tài)聯(lián)系還是很緊密的,比如Waiting(無(wú)限等待) 狀態(tài)中wait方法是空參的,而timed waiting(計(jì)時(shí)等待) 中wait方法是帶參的。這種帶參的方法,其實(shí)是一種倒計(jì)時(shí)操作,相當(dāng)于我們生活中的小鬧鐘,我們?cè)O(shè)定好時(shí)間,到時(shí)通知,可是如果提前得到(喚醒)通知,那么設(shè)定好時(shí)間在通知也就顯得多此一舉了,那么這種設(shè)計(jì)方案其實(shí)是一舉兩得。如果沒(méi)有得到(喚醒)通知,那么線(xiàn)程就處于Timed Waiting狀態(tài),直到倒計(jì)時(shí)完畢自動(dòng)醒來(lái);如果在倒計(jì)時(shí)期間得到(喚醒)通知,那么線(xiàn)程從Timed Waiting狀態(tài)立刻喚醒。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。