您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“Java線程的6種狀態與生命周期是什么”,內容詳細,步驟清晰,細節處理妥當,希望這篇“Java線程的6種狀態與生命周期是什么”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
一個線程在給定的時間點只能處于一種狀態。
線程可以有如下6 種狀態:
New (新創建):未啟動的線程;
Runnable (可運行):可運行的線程,需要等待操作系統資源;
Blocked (被阻塞):等待監視器鎖而被阻塞的線程;
Waiting (等待):等待喚醒狀態,無限期地等待另一個線程喚醒;
Timed waiting (計時等待):在指定的等待時間內等待另一個線程執行操作的線程;
Terminated (被終止):已退出的線程。
要確定一個線程的當前狀態, 可調用getState 方法
線程狀態關系圖
注意:虛線框(全大寫英文)的狀態為Java線程狀態。
就是實例化線程完成后,未啟動線程的狀態。
可通過三種方式創建線程
重寫Thread類run()方法
實現Runnable接口
實現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、內部類或線程類方式實現Runnable接口,實現run()方法 * 再交給Thread 類 */ Thread runThread = new Thread(() -> { System.out.println("Runnable"); }); // 開啟線程 runThread.start(); /** * 3.lambda、內部類或線程類方式實現Callable接口,實現call()方法 * 再交給Thread 類:FutureTask本質也是Runnable實現類 */ 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類創建的線程沒有實際意義;
只有Callable方式創建的線程可以獲取線程的返回值。
該狀態指的是線程實例化對象調用start()方法后進入的狀態。線程處于可以運行狀態,如果有處理器等資源,就可以執行程序。
該狀態在操作系統層面包含兩步:線程就緒和線程運行中,但在Java線程狀態中,這兩步都統稱為Runnable(可運行)狀態。
線程由就緒狀態變為運行狀態,重點就看你的線程有沒有搶到CPU資源(CPU時間片),誰搶到就運行,沒搶到就等。因為CPU時間片(執行時間)非常短,大概十幾毫秒,所以線程切換的這個時間是非常短的,就緒狀態變為運行狀態的時間也非常短,在開發時幾乎感覺不到這種狀態的變化,所以在Java中將兩者看作是一個整體,重點關注線程可否運行并區別于其他狀態即可,更進一步簡化線程的開發。如果你的程序要運行很久(比如寫個死循環),在一個CPU時間片內沒有執行完成,那么你的線程就要搶下一次的CPU時間片,搶到了才可以繼續執行程序,沒搶到那就要繼續搶,直到線程中的程序執行完成。
其實這個場景應該都見到過,例如多個線程執行同一個程序,都將日志打印到同一個文件時,就會出現不同線程的日志混在了一起的情況,不利于排查問題。解決這種問題常見的方法有:一是分線程打印日志到不同文件;二是將日志信息保存到字符串對象中,在程序的最后將日志信息一次性打印到文件。第二種方式就是利用CPU的一個時間片來完成日志信息的打印。
注意:程序只能對新建狀態的線程調用start()方法,不要對處于非新建狀態的線程調用start() 方法,這都會引發IllegalThreadStateException異常。
線程處于等待監視器鎖而被阻塞的狀態。有一個線程獲取了鎖未釋放,其他線程也來獲取,但發現獲取不到鎖也進入了被阻塞狀態。
被阻塞狀態只存在于多線程并發訪問下,區別于后面兩種因線程自己進入”等待“而導致的阻塞。
進入狀態
進入synchronized 代碼塊/方法
未獲取到鎖
退出狀態
獲取到監視器鎖
整個流程是這樣的:線程在某個對象的同步方法中先獲取到對象鎖;在執行wait方法時,該線程將釋放對象鎖,并且該線程被放入到這個對象的等待隊列;等待另一個線程獲取到同一個對象的鎖,然后通過notify() 或 notifyAll() 方法喚醒對象等待隊列中的線程。
從整個流程可以知道
wait (),notify () 和 notifyAll () 方法需要在線程獲取到鎖的情況下才可以繼續執行,所以這三個方法都需要放在同步代碼塊/方法中執行,否則報異常:java.lang.IllegalMonitorStateException。
在同步代碼塊中,線程進入WAITING 狀態時,鎖會被釋放,不會導致該線程阻塞。反過來想下,如果鎖沒釋放,那其他線程就沒辦法獲取鎖,也就沒辦法喚醒它。
進入狀態
object.wait()
thread.join()
LockSupport.park()
退出狀態
object.notify()
object.notifyall()
LockSupport.unpark()
一般是計時結束就會自動喚醒線程繼續執行后面的程序,對于Object.wait(long) 方法還可以主動通知喚醒。
注意:Thread類下的sleep() 方法可以放在任意地方執行;而wait(long) 方法和wait() 方法一樣,需要放在同步代碼塊/方法中執行,否則報異常:java.lang.IllegalMonitorStateException。
進入狀態
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 參數為負責此線程駐留的同步對象。
退出狀態
計時結束
LockSupport.unpark(Thread)
object.notify()
object.notifyall()
線程執行結束
run()/call() 執行完成
stop()線程
錯誤或異常>>意外死亡
stop() 方法已棄用。
通過一個簡單的例子來查看線程出現的6種狀態。
案例
public class Demo3 { private static Object object ="obj"; public static void main(String[] args) throws InterruptedException { Thread thread0 = new Thread(() -> { try { // 被阻塞狀態(BLOCKED) synchronized (object){ System.out.println("thread0 進入:等待喚醒狀態(WAITING)"); object.wait(); System.out.println("thread0 被解除完成:等待喚醒狀態(WAITING)"); } System.out.println("thread0 "+Thread.currentThread().getState()); } catch (InterruptedException e) { e.printStackTrace(); } }); // 新創建狀態(NEW) System.out.println(thread0.getName()+":"+thread0.getState()); Thread thread1 = new Thread(() -> { try { System.out.println("thread1 進入:計時等待狀態(TIMED_WAITING)"); Thread.sleep(2); System.out.println("thread1 出來:計時等待狀態(TIMED_WAITING)"); } catch (InterruptedException e) { e.printStackTrace(); } // 被阻塞狀態(BLOCKED) synchronized (object){ System.out.println("thread1 解除:等待喚醒狀態(WAITING)"); object.notify(); System.out.println("thread1 解除完成:等待喚醒狀態(WAITING)"); } System.out.println("thread1 "+Thread.currentThread().getState()); }); // 新創建狀態(NEW) System.out.println(thread1.getName()+":"+thread1.getState()); printState(thread0); printState(thread1); // 可運行狀態(RUNNABLE) thread0.start(); // 可運行狀態(RUNNABLE) thread1.start(); } // 使用獨立線程來打印線程狀態 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(); } }
執行結果:簡化后的輸出結果
Thread-0:NEW
Thread-1:NEW
Thread-0:RUNNABLE
Thread-1:RUNNABLE
thread0 進入:等待喚醒狀態(WAITING)
Thread-1:BLOCKED
thread1 進入:計時等待狀態(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 出來:計時等待狀態(TIMED_WAITING)
Thread-0:WAITING
Thread-1:BLOCKED
thread1 解除:等待喚醒狀態(WAITING)
Thread-1:BLOCKED
Thread-0:WAITING
Thread-0:BLOCKED
thread1 解除完成:等待喚醒狀態(WAITING)
Thread-1:BLOCKED
thread1 RUNNABLE
Thread-0:BLOCKED
Thread-1:TERMINATED
thread0 被解除完成:等待喚醒狀態(WAITING)
Thread-0:BLOCKED
thread0 RUNNABLE
Thread-0:TERMINATED
最終的執行結果如圖。
注意:因為案例中使用了獨立線程來打印不同線程的狀態,會出現狀態打印稍微延遲的情況。
讀到這里,這篇“Java線程的6種狀態與生命周期是什么”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。