一文詳解Java線程的6種狀態(tài)與生命周期
1.線程狀態(tài)(生命周期)
一個線程在給定的時間點只能處于一種狀態(tài)。
線程可以有如下6 種狀態(tài):
- New (新創(chuàng)建):未啟動的線程;
- Runnable (可運行):可運行的線程,需要等待操作系統(tǒng)資源;
- Blocked (被阻塞):等待監(jiān)視器鎖而被阻塞的線程;
- Waiting (等待):等待喚醒狀態(tài),無限期地等待另一個線程喚醒;
- Timed waiting (計時等待):在指定的等待時間內(nèi)等待另一個線程執(zhí)行操作的線程;
- Terminated (被終止):已退出的線程。
要確定一個線程的當(dāng)前狀態(tài), 可調(diào)用getState 方法
線程狀態(tài)關(guān)系圖
注意:虛線框(全大寫英文)的狀態(tài)為Java線程狀態(tài)。
2.操作線程狀態(tài)
2.1.新創(chuàng)建狀態(tài)(NEW)
就是實例化線程完成后,未啟動線程的狀態(tài)。
可通過三種方式創(chuàng)建線程
- 重寫Thread類run()方法
- 實現(xiàn)Runnable接口
- 實現(xiàn)Callable接口
一個簡單的例子概括三種方式
public?class?Demo?{ ????public?static?void?main(String[]?args)?throws?ExecutionException,?InterruptedException?{ ????????/** ?????????*?1.直接重寫run()?或繼承Thread類再重寫run() ?????????*/ ????????Thread?thread?=?new?Thread()?{ ????????????@Override ????????????public?void?run()?{ ????????????????System.out.println("Thread"); ????????????} ????????}; ????????//?開啟線程 ????????thread.start(); ????????/** ?????????*?2.lambda、內(nèi)部類或線程類方式實現(xiàn)Runnable接口,實現(xiàn)run()方法 ?????????*?再交給Thread?類 ?????????*/ ????????Thread?runThread?=?new?Thread(()?->?{ ????????????System.out.println("Runnable"); ????????}); ????????//?開啟線程 ????????runThread.start(); ????????/** ?????????*?3.lambda、內(nèi)部類或線程類方式實現(xiàn)Callable接口,實現(xiàn)call()方法 ?????????*?再交給Thread 類:FutureTask本質(zhì)也是Runnable實現(xiàn)類 ?????????*/ ????????FutureTask<String>?futureTask?=?new?FutureTask<String>(()?->?{ ????????????System.out.println("Callable"); ????????????return?"CallableThread"; ????????}); ????????Thread?callThread?=?new?Thread(futureTask); ????????//?開啟線程 ????????callThread.start(); ????????//?獲取call()方法的返回值 ????????String?s?=?futureTask.get(); ????????System.out.println("call()方法的返回值:"+s); ????} }
不重寫 run() 或 call() 方法直接實例化Thread類創(chuàng)建的線程沒有實際意義;
只有Callable方式創(chuàng)建的線程可以獲取線程的返回值。
2.2.可運行狀態(tài)(RUNNABLE)
該狀態(tài)指的是線程實例化對象調(diào)用start()方法后進(jìn)入的狀態(tài)。線程處于可以運行狀態(tài),如果有處理器等資源,就可以執(zhí)行程序。
該狀態(tài)在操作系統(tǒng)層面包含兩步:線程就緒和線程運行中,但在Java線程狀態(tài)中,這兩步都統(tǒng)稱為Runnable(可運行)狀態(tài)。
線程由就緒狀態(tài)變?yōu)檫\行狀態(tài),重點就看你的線程有沒有搶到CPU資源(CPU時間片),誰搶到就運行,沒搶到就等。因為CPU時間片(執(zhí)行時間)非常短,大概十幾毫秒,所以線程切換的這個時間是非常短的,就緒狀態(tài)變?yōu)檫\行狀態(tài)的時間也非常短,在開發(fā)時幾乎感覺不到這種狀態(tài)的變化,所以在Java中將兩者看作是一個整體,重點關(guān)注線程可否運行并區(qū)別于其他狀態(tài)即可,更進(jìn)一步簡化線程的開發(fā)。如果你的程序要運行很久(比如寫個死循環(huán)),在一個CPU時間片內(nèi)沒有執(zhí)行完成,那么你的線程就要搶下一次的CPU時間片,搶到了才可以繼續(xù)執(zhí)行程序,沒搶到那就要繼續(xù)搶,直到線程中的程序執(zhí)行完成。
其實這個場景應(yīng)該都見到過,例如多個線程執(zhí)行同一個程序,都將日志打印到同一個文件時,就會出現(xiàn)不同線程的日志混在了一起的情況,不利于排查問題。解決這種問題常見的方法有:一是分線程打印日志到不同文件;二是將日志信息保存到字符串對象中,在程序的最后將日志信息一次性打印到文件。第二種方式就是利用CPU的一個時間片來完成日志信息的打印。
注意:程序只能對新建狀態(tài)的線程調(diào)用start()方法,不要對處于非新建狀態(tài)的線程調(diào)用start() 方法,這都會引發(fā)IllegalThreadStateException異常。
2.3.被阻塞狀態(tài)(BLOCKED)
線程處于等待監(jiān)視器鎖而被阻塞的狀態(tài)。有一個線程獲取了鎖未釋放,其他線程也來獲取,但發(fā)現(xiàn)獲取不到鎖也進(jìn)入了被阻塞狀態(tài)。
被阻塞狀態(tài)只存在于多線程并發(fā)訪問下,區(qū)別于后面兩種因線程自己進(jìn)入”等待“而導(dǎo)致的阻塞。
進(jìn)入狀態(tài)
- 進(jìn)入synchronized 代碼塊/方法
- 未獲取到鎖
退出狀態(tài)
- 獲取到監(jiān)視器鎖
2.4.等待喚醒狀態(tài)(WAITING)
整個流程是這樣的:線程在某個對象的同步方法中先獲取到對象鎖;在執(zhí)行wait方法時,該線程將釋放對象鎖,并且該線程被放入到這個對象的等待隊列;等待另一個線程獲取到同一個對象的鎖,然后通過notify() 或 notifyAll() 方法喚醒對象等待隊列中的線程。
從整個流程可以知道
wait (),notify () 和 notifyAll () 方法需要在線程獲取到鎖的情況下才可以繼續(xù)執(zhí)行,所以這三個方法都需要放在同步代碼塊/方法中執(zhí)行,否則報異常:java.lang.IllegalMonitorStateException。
在同步代碼塊中,線程進(jìn)入WAITING 狀態(tài)時,鎖會被釋放,不會導(dǎo)致該線程阻塞。反過來想下,如果鎖沒釋放,那其他線程就沒辦法獲取鎖,也就沒辦法喚醒它。
進(jìn)入狀態(tài)
- object.wait()
- thread.join()
- LockSupport.park()
退出狀態(tài)
- object.notify()
- object.notifyall()
- LockSupport.unpark()
2.5.計時等待狀態(tài)(TIMED_WAITING)
一般是計時結(jié)束就會自動喚醒線程繼續(xù)執(zhí)行后面的程序,對于Object.wait(long) 方法還可以主動通知喚醒。
注意:Thread類下的sleep() 方法可以放在任意地方執(zhí)行;而wait(long) 方法和wait() 方法一樣,需要放在同步代碼塊/方法中執(zhí)行,否則報異常:java.lang.IllegalMonitorStateException。
進(jìn)入狀態(tài)
- Thread.sleep(long)
- Object.wait(long)
- Thread.join(long)
- LockSupport.parkNanos(long)
- LockSupport.parkNanos(Object blocker, long nanos)
- LockSupport.parkUntil(long)
- LockSupport.parkUntil(Object blocker, long deadline)
注:blocker 參數(shù)為負(fù)責(zé)此線程駐留的同步對象。
退出狀態(tài)
- 計時結(jié)束
- LockSupport.unpark(Thread)
- object.notify()
- object.notifyall()
2.6.終止(TERMINATED)
線程執(zhí)行結(jié)束
- run()/call() 執(zhí)行完成
- stop()線程
- 錯誤或異常>>意外死亡
stop() 方法已棄用。
3.查看線程的6種狀態(tài)
通過一個簡單的例子來查看線程出現(xiàn)的6種狀態(tài)。
案例
public?class?Demo3?{ ????private?static?Object?object?="obj"; ???? ????public?static?void?main(String[]?args)?throws?InterruptedException?{ ????????Thread?thread0?=?new?Thread(()?->?{ ????????????try?{ ????????????????//?被阻塞狀態(tài)(BLOCKED) ????????????????synchronized?(object){ ????????????????????System.out.println("thread0?進(jìn)入:等待喚醒狀態(tài)(WAITING)"); ????????????????????object.wait(); ????????????????????System.out.println("thread0?被解除完成:等待喚醒狀態(tài)(WAITING)"); ????????????????} ????????????????System.out.println("thread0?"+Thread.currentThread().getState()); ????????????}?catch?(InterruptedException?e)?{ ????????????????e.printStackTrace(); ????????????} ????????}); ????????//?新創(chuàng)建狀態(tài)(NEW) ????????System.out.println(thread0.getName()+":"+thread0.getState()); ????????Thread?thread1?=?new?Thread(()?->?{ ????????????try?{ ????????????????System.out.println("thread1 進(jìn)入:計時等待狀態(tài)(TIMED_WAITING)"); ????????????????Thread.sleep(2); ????????????????System.out.println("thread1 出來:計時等待狀態(tài)(TIMED_WAITING)"); ????????????}?catch?(InterruptedException?e)?{ ????????????????e.printStackTrace(); ????????????} ????????????//?被阻塞狀態(tài)(BLOCKED) ????????????synchronized?(object){ ????????????????System.out.println("thread1 解除:等待喚醒狀態(tài)(WAITING)"); ????????????????object.notify(); ????????????????System.out.println("thread1 解除完成:等待喚醒狀態(tài)(WAITING)"); ????????????} ????????????System.out.println("thread1?"+Thread.currentThread().getState()); ????????}); ????????//?新創(chuàng)建狀態(tài)(NEW) ????????System.out.println(thread1.getName()+":"+thread1.getState()); ????????printState(thread0); ????????printState(thread1); ????????//?可運行狀態(tài)(RUNNABLE) ????????thread0.start(); ????????//?可運行狀態(tài)(RUNNABLE) ????????thread1.start(); ????} ???? ???? ????//?使用獨立線程來打印線程狀態(tài) ????private?static?void?printState(Thread?thread)?{ ????????new?Thread(()->{ ????????????while?(true){ ????????????????System.out.println(thread.getName()+":"+thread.getState()); ????????????????if?(thread.getState().equals(Thread.State.TERMINATED)){ ????????????????????System.out.println(thread.getName()+":"+thread.getState()); ????????????????????break; ????????????????} ????????????} ????????}).start(); ????} }
執(zhí)行結(jié)果:簡化后的輸出結(jié)果
Thread-0:NEW
Thread-1:NEW
Thread-0:RUNNABLE
Thread-1:RUNNABLE
thread0 進(jìn)入:等待喚醒狀態(tài)(WAITING)
Thread-1:BLOCKED
thread1 進(jìn)入:計時等待狀態(tài)(TIMED_WAITING)
Thread-0:BLOCKED
Thread-0:WAITING
……
Thread-0:WAITING
Thread-1:BLOCKED
Thread-1:TIMED_WAITING
……
Thread-1:TIMED_WAITING
Thread-1:BLOCKED
……
Thread-1:BLOCKED
Thread-0:WAITING
……
Thread-0:WAITING
thread1 出來:計時等待狀態(tài)(TIMED_WAITING)
Thread-0:WAITING
Thread-1:BLOCKED
thread1 解除:等待喚醒狀態(tài)(WAITING)
Thread-1:BLOCKED
Thread-0:WAITING
Thread-0:BLOCKED
thread1 解除完成:等待喚醒狀態(tài)(WAITING)
Thread-1:BLOCKED
thread1 RUNNABLE
Thread-0:BLOCKED
Thread-1:TERMINATED
thread0 被解除完成:等待喚醒狀態(tài)(WAITING)
Thread-0:BLOCKED
thread0 RUNNABLE
Thread-0:TERMINATED
最終的執(zhí)行結(jié)果如圖。
注意:因為案例中使用了獨立線程來打印不同線程的狀態(tài),會出現(xiàn)狀態(tài)打印稍微延遲的情況。
到此這篇關(guān)于一文詳解Java線程的6種狀態(tài)與生命周期的文章就介紹到這了,更多相關(guān)Java線程狀態(tài)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java如何實現(xiàn)支付寶電腦支付基于servlet版本
這篇文章主要介紹了Java如何實現(xiàn)支付寶電腦支付基于servlet版本,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-11-11java多線程編程必備volatile與synchronized深入理解
這篇文章主要介紹了java多線程編程必備volatile與synchronized的深入理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04