Java執行緒的生命週期
目前CPU的運算速度已經達到了百億次每秒,甚至更高的量級,家用電腦即使維持作業系統正常執行的程序也會有數十個,執行緒更是數以百計。
執行緒是CPU的排程和分派的基本單位,為了更充分地利用CPU資源以及提高生產率和高效地完成任務,在現實場景中一般都會採用多執行緒處理。
執行緒的生命週期
執行緒的生命週期大致可以分為下面五種狀態: New (新建狀態)、 RUNABLE (就緒狀態)、 RUNNING (執行狀態)、 休眠狀態 、 DEAD (終止狀態)

1、 新建狀態 ,是執行緒被建立且未啟動的狀態;這裡的建立,僅僅是在JAVA的這種程式語言層面被建立,而在作業系統層面,真正的執行緒還沒有被建立。
Thread t1 = new Thread() 複製程式碼
2、 就緒狀態 ,指的是呼叫start()方法之後,執行緒等待分配給CPU執行(這時候,執行緒已經在作業系統層面被建立)
t1.start() 複製程式碼
3、 執行狀態 ,當CPU空閒時,執行緒被分得CPU時間片,執行Run()方法的狀態
4、 休眠狀態 ,執行狀態的執行緒,如果呼叫一個阻塞的API或者等待某個事件,那麼執行緒的狀態就會轉換到休眠狀態,一般有以下幾種情況
- 同步阻塞:鎖被其它執行緒佔用
- 主動阻塞:呼叫Thread的某些方法,主動讓出CPU執行權,比如:sleep()、join()等方法
- 等待阻塞:執行了wait()方法
5、 終止狀態 ,執行緒執行完( run()方法執行結束
)或者出現異常就會進入終止狀態
對應的是JAVA中Thread類 State
中的六種狀態
public class Thread implements Runnable { //......... public enum State { NEW, // 初始化狀態 RUNNABLE, // 可執行/執行狀態 BLOCKED, // 阻塞狀態 WAITING, // 無時限等待 TIMED_WAITING, // 有時限等待 TERMINATED; // 終止狀態 } // .......... } 複製程式碼
休眠狀態(BLOCKED、WAITING、TIMED_WAITING)與RUNNING狀態的轉換
1、RUNNING狀態與BLOCKED狀態的轉換
-
執行緒 等待 synchronized 的隱式鎖, RUNNING —> BLOCKED
-
執行緒 獲得 synchronized 的隱式鎖, BLOCKED —> RUNNING
2、RUNNING狀態與WAITING狀態的轉換
- 獲得 synchronized 隱式鎖的執行緒,呼叫無引數的Object.wait()方法
- 呼叫無引數Thread.join()方法
- 呼叫LockSupport.park()方法,執行緒阻塞切換到 WAITING 狀態,
- 呼叫LockSupport.unpark()方法,可喚醒執行緒,從 WAITING 狀態切換到 RUNNING 狀態
3、RUNNING狀態與TIMED_WAITING狀態的轉換
- 呼叫 帶超時引數 的 Thread..sleep(long millis)方法
- 獲得 synchronized 隱式鎖的執行緒,呼叫 帶超時引數 的Object.wait(long timeout)方法
- 呼叫 帶超時引數 的Thread.join(long millis)方法
- 呼叫 帶超時引數 的LockSupport.parkNanos(Object blocker,long deadline)方法
- 呼叫 帶超時引數 的LockSupport.parkUntil(long deadline)方法
廢棄掉的執行緒方法 :stop()、suspend()、resume()
stop()
方法,會真正的殺死執行緒,不給執行緒任何喘息的機會,假設獲得 synchronized 隱式鎖的執行緒,此刻執行 stop()
方法,該鎖不會被釋放,導致其它執行緒沒有任何機會獲得鎖,顯然這樣的結果不是我們想要見到的。
suspend()
和 resume()
方法同樣,因為某種不可預料的原因,已經被建議不在使用
不能使用 stop()、suspend() 、resume()
這些方法來終止執行緒或者喚醒執行緒,那麼我們應該使用什麼方法來做呢?答案是:優雅的使用 Thread.interrupt()
方法來做
優雅的Thread.interrupt()方法中斷執行緒
interrupt()
方法僅僅是通知執行緒,執行緒有機會執行一些後續操作,同時也可以無視這個通知,這個方法通過修改了呼叫執行緒的中斷狀態來告知那個執行緒,說它被中斷了,執行緒可以通過 isInterrupted()
方法,檢測是不是自己被中斷。
當執行緒被阻塞的時候,比如被Object.wait, Thread.join和Thread.sleep三種方法之一阻塞時;呼叫它的 interrput()
方法,會產生 InterruptedException異常 。
擴充套件知識點
探祕區域性變數不會引發併發問題的原因
在Java領域, 執行緒 可以擁有自己的 運算元棧,程式計數器、區域性變量表 等資源;我們都知道,多個執行緒同時訪問 共享變數 的時候,會導致資料不一致性等併發問題;但是 Java 方法裡面的區域性變數是不存在併發問題的,具體為什麼呢?我們先來了解一下這些基礎知識。
區域性變數的作用域是方法內部的,當方法執行完了,區域性變數也就銷燬了,也就是說 區域性變數應該是和方法同生共死 的。
Java中的方法是如何呼叫的?當呼叫方法時,會 建立新的棧幀 ,並壓入呼叫棧;當方法返回時,對應的棧幀就會被自動彈出。也就是說,棧幀和方法是同生共死的。
從上面我們可以得出: 方法的呼叫就是壓棧和出棧的過程 ,而在Java中的方法的 區域性變數 又是儲存在 棧幀中 ,所以我們用下面的示意圖幫助大家理解一下

說了那麼多,我們還沒有解釋區域性變數為啥不會產生併發問題,以上,我們知道了,方法的呼叫是壓棧和出棧(棧幀)的過程,區域性變數又儲存在棧幀中。那麼我們的執行緒和呼叫棧又有什麼關係呢,答案是: 每個執行緒都有自己獨立的呼叫棧

到現在,相信大家都已經明白了,區域性變數之所以不存在併發問題,是因為,每個執行緒都有自己的呼叫棧,區域性變數都儲存線上程各自的呼叫棧裡面,沒有共享,自然就不存在併發問題。
歡迎大家關注公眾號: 小白程式之路(whiteontheroad) ,第一時間獲取最新資訊!!!
