【Java 併發編程系列3】線程狀態 & 守護線程


一、線程狀態說明

Java 的線程在運行階段會有不一樣的狀態,在Java代碼中表現爲一個枚舉類 Thread.State 其定義以下:
前端

public enum State {
 // 還沒有啓動的線程狀態,通常是建立好的線程還沒有調用start() 方法局處於NEW狀態
 NEW,
       
 // 線程處於可運行的狀態,表示能夠被JVM執行,可是也可能處於等待其餘資源的狀態,例如等待處理器的資源
 // JVM 概述的將可運行與運行中,統稱爲RUNABLE   
 RUNNABLE,
       
 // 線程處於阻塞狀態,表示線程正在等待獲取鎖,通常是synchronized代碼或者調用某個對象wait()方法
 // 暫時獲取不到鎖,處於其餘資源釋放鎖的等待狀態
 BLOCKED,
       
 /**
  * 線程狀態處於等待狀態,多是調用如下的方法:
  *   <li>{@link Object#wait() Object.wait}</li>
  *   <li>{@link #join() Thread.join} with no timeout</li>
  *   <li>{@link LockSupport#park() LockSupport.park}</li>
  *
  * 或者是處於等待狀態的線程在等待其餘線程執行特定的動做,好比當前線程調用了某個對象的Object.wait()方法 
  * 該線程將會等待其餘線程調用該對象的Object.notify()方法或者Object.notifyAll()方法
  * 或者調用了Thread.join()方法的線程等待其餘線程的完成,即等待其餘線程進入TERMINATED狀態
  */
 WAITING,
       
 /**
  * 線程處於有限時間的等待,通常是調用的了包含時間的等待放方法,好比下面的方法
    {@link #sleep Thread.sleep} 休眠了必定的時間
    {@link Object#wait(long) Object.wait}
    {@link #join(long) Thread.join} with timeout
    {@link LockSupport#parkNanos LockSupport.parkNanos}
    {@link LockSupport#parkUntil LockSupport.parkUntil}
  */
 TIMED_WAITING,
       
 /**
  * 線程處於消亡狀態,表示線程已經執行完成。
  */
 TERMINATED;

二、線程狀態的切換流程


經過上文的枚舉狀態說明,咱們瞭解到Java程序運行的代碼狀態的具體含義,線程在自身切換的生命週期中,並非固定處於某個狀態,而是隨着代碼的執行在不停地切換,下圖概述了Java線程的狀態切換流程。
java

在這裏插入圖片描述

  • 建立新的線程,在調用start()方法以前狀態爲 NEW
  • 調用start()方法,線程由 NEW 進入 RUNNABLE 狀態,JVM籠統的將正在運行和等待CPU執行統稱爲 RUNNABLE
  • 若是調用 Object.wait() 或者LockSupport.park() 方法則進入WAITING狀態,當其餘線程調用Object.notify()或者Obeject.notifyAll() 方法時候,線程可能會恢復爲 RUNABLE , 若是設置等待超時參數,則會進入 TIMED_WAITING 狀態
好比多個線程都在等待某個對象的notify()方法或者notifyAll()被調用,在恢復的時候可能就是隨機選擇一個等待該對象nofify()的線程恢復,其餘線程繼續等待,因此此處才聲明有可能恢復爲RUNABLE狀態
  • 調用 Thread.join() 方法也會進入WAITING或者TIMED_WAITING狀態,只有在對應的線程消亡後,纔會跳出 WAITING 或者TIMED_WAITING 狀態


  • 當程序進入同步方法或者同步代碼塊的時候,即進入 synchronized 關鍵字修飾的方法或者代碼塊的時候,則進入阻塞狀態
通常其餘的Lock同步的話,則會進入WAITING或則TIMED_WAITING 狀態,而非 BLOCKED 狀態
  • 程序執行完成後進入 TERINATED 狀態

三、守護線程

顧名思義,守護線程就是守護其餘線程的執行,當進程沒有非守護進程的時候,JVM就會退出 (只有進程中存在非守護線程,VM纔不會退出)。它主要用於在程序後臺進行調度以及其餘支持。能夠經過 Thread.setDaemon(bool) 來設置當前線程是是守護線程(傳參true)仍是用戶線程(傳參false)。該設置僅在Thread處於NEW狀態時候設置有效,不能在線程啓動以後設置。若是設置則會拋出異常 IllegalThreadStateException
算法

public final void setDaemon(boolean on) {
    checkAccess();
    if (isAlive()) {
        throw new IllegalThreadStateException();
    }
    daemon = on;
}

/**
 * Tests if this thread is alive. A thread is alive if it has
 * been started and has not yet died.
 */
public final native boolean isAlive();

當JVM退出的時候,守護線程並不必定會執行完畢,好比並不會執行finally方法,因此咱們並不能依賴守護線程的finally方法來進行資源的關閉或者釋放。好比下面的代碼
編程

public class DaemonThread implements Runnable {
  @Override
  public void run() {
    try {
      SleepUtils.sleep(2);
    } finally {
      // 此代碼並不會被執行
      System.out.println("DaemonThread.run.finally");
    }
  }

  public static void main(String[] args) {
    Thread thread = new Thread(new DaemonThread(), "DaemonThread");
    thread.setDaemon(true);
    thread.start();
  }
}


在上一篇文章線程的建立,展現了一段線程建立的init() 方法的代碼,裏面的代碼展現了線程的是不是守護線程會被繼承到其建立的線程的,也就是說守護線程建立的線程,默認也是守護線程後端

this.daemon = parent.isDaemon();
相關文章
相關標籤/搜索