天天看點

Java線程狀态

線程跟人類一樣擁有自己的生命周期,一條線程從建立到執行完畢的過程即是線程的生命周期,此過程可能在不同時刻處于不同的狀态,線程狀态正是這小節的主題,線程到底有多少種狀态?不同狀态之間是如何轉化的?

對于線程的狀态的分類并沒有嚴格的規定,隻要能正确表示狀态即可,如圖2-5-7-1,先看其中一種狀态分類,一個線程從建立到死亡可能會經曆若幹個狀态,但在任意一個時間點線程隻能處于其中一種狀态,總共包含五個狀态:建立(new)、可運作(runnable)、運作(running)、非可運作(not runnable)、死亡(dead)。線程的狀态的轉化可以由程式控制,通過某些API可以達到轉化效果,例如Thread類的start、stop、sleep、suspend、resume、wait、notify等方法(stop、suspend、resume等方法因為容易引起死鎖問題而早已被棄用)。

Java線程狀态

圖2-5-7-1

l   建立(new):一個線程被建立了但未被啟動就處于建立狀态,即在程式中使用new MyThread();建立的線程執行個體就處于此狀态。

l   可運作(runnable):建立的線程執行個體調用start()方法後便進入可運作狀态,處于此狀态的線程并不是說一定處于運作狀态,我們在上一節多線程排程政策了解到Java多線程使用的是搶占式排程,每個可運作線程輪着擷取CPU時間片,可以虛拟想象成有一個可運作線程池,start()方法把線程放進可運作線程池中,CPU按一定規則一個個執行池裡的線程。

l   運作(running):當可運作線程擷取到CPU執行時間片即進去了運作狀态。

l   非可運作(notrunnable):運作中的線程因某種原因暫時放棄CPU的使用權,可能是因為執行了挂起、睡眠或等待等操作,在執行I/O操作時由于外部裝置速度遠低于處理器速度也可能導緻線程暫時放棄CPU使用權,在擷取對象的同步鎖過程中如果同步鎖先被别的線程占用同樣可能導緻線程暫時放棄CPU。

l   死亡(dead):線程執行完run()方法實作的任務,或因為異常導緻退出任務,線程進入死亡狀态後将不可再轉換成其他狀态。

将非可運作(not runnable)狀态繼續細分,如圖2-5-7-2,建立、可運作、運作、死亡四個狀态的定義和轉化與前面的一樣,重點看非可運作狀态引申出來的三個狀态:阻塞(blocked)、同步鎖(locked)、等待(waiting)。

l   阻塞(blocked):阻塞由阻塞事件觸發,線程處于阻塞狀态将放棄CPU的使用權,暫時停止運作。一般線程執行了sleep()、join()方法,或發出了I/O請求,線程就将處于阻塞狀态,假如sleep()執行的睡眠結束、join()執行的等待中斷逾時、I/O請求結束,則将重新回到可執行狀态,等待配置設定CPU。

l   同步鎖(locked):假如一個線程準備調用一個同步方法,而同步方法對應的對象正被其他線程占用,此時線程就将進入同步鎖狀态。實際上,Java中的每個object對象都有一個monitor,此monitor負責對同步域在并發時的獨占處理,即一個線程調用某對象的同步方法時,JVM将檢測改對象的monitor是否已被占用,如果沒有被占用,線程則得到monitor占有權,繼續執行該對象的同步方法,否則線程将被扔進一個等待線程隊列排隊,直到monitor被釋放後,所有等待的線程繼續競争monitor占有權,搶到monitor占有權後才進入可執行狀态等待CPU的配置設定,才有資格執行同步方法。

l   等待(waiting):運作中的線程執行了wait()方法後就進入等待狀态。一個對象執行了wait()方法同樣将使線程進入該對象的等待線程隊列,同時它還将釋放對象鎖,即放棄monitor的占有權。隻有在其他線程中對該對象調用notify()、notifyAll()方法時才會喚醒等待線程隊列中的線程,notify是随機喚醒等待隊列中的一個線程,而nofityAll則是喚醒所有等待隊列中的線程,所有線程被喚醒後将對該對象的monitor占有權競争,擷取到占有權的線程才能轉化為可執行狀态,等待配置設定CPU往下執行,其他線程則繼續等待。

Java線程狀态

圖2-5-7-2

<a target="_blank" href="https://item.jd.com/12185360.html">點選訂購作者書籍《Tomcat核心設計剖析》</a>