線程跟人類1樣具有自己的生命周期,1條線程從創建到履行終了的進程即是線程的生命周期,此進程可能在不同時刻處于不同的狀態,線程狀態正是這小節的主題,線程到底有多少種狀態?不同狀態之間是如何轉化的?
對線程的狀態的分類并沒有嚴格的規定,只要能正確表示狀態便可,如圖2⑸⑺⑴,先看其中1種狀態分類,1個線程從創建到死亡可能會經歷若干個狀態,但在任意1個時間點線程只能處于其中1種狀態,總共包括5個狀態:新建(new)、可運行(runnable)、運行(running)、非可運行(not runnable)、死亡(dead)。線程的狀態的轉化可以由程序控制,通過某些API可以到達轉化效果,例如Thread類的start、stop、sleep、suspend、resume、wait、notify等方法(stop、suspend、resume等方法由于容易引發死鎖問題而早已被棄用)。
圖2⑸⑺⑴
l 新建(new):1個線程被創建了但未被啟動就處于新建狀態,即在程序中使用new MyThread();創建的線程實例就處于此狀態。
l 可運行(runnable):創建的線程實例調用start()方法后便進入可運行狀態,處于此狀態的線程其實不是說1定處于運行狀態,我們在上1節多線程調度策略了解到Java多線程使用的是搶占式調度,每一個可運行線程輪著獲得CPU時間片,可以虛擬想象成有1個可運行線程池,start()方法把線程放進可運行線程池中,CPU按1定規則1個個履行池里的線程。
l 運行(running):當可運行線程獲得到CPU履行時間片即進去了運行狀態。
l 非可運行(notrunnable):運行中的線程因某種緣由暫時放棄CPU的使用權,多是由于履行了掛起、睡眠或等待等操作,在履行I/O操作時由于外部裝備速度遠低于處理器速度也可能致使線程暫時放棄CPU使用權,在獲得對象的同步鎖進程中如果同步鎖先被別的線程占用一樣可能致使線程暫時放棄CPU。
l 死亡(dead):線程履行完run()方法實現的任務,或由于異常致使退出任務,線程進入死亡狀態后將不可再轉換成其他狀態。
將非可運行(not runnable)狀態繼續細分,如圖2⑸⑺⑵,新建、可運行、運行、死亡4個狀態的定義和轉化與前面的1樣,重點看非可運行狀態引伸出來的3個狀態:阻塞(blocked)、同步鎖(locked)、等待(waiting)。
l 阻塞(blocked):阻塞由阻塞事件觸發,線程處于阻塞狀態將放棄CPU的使用權,暫時停止運行。1般線程履行了sleep()、join()方法,或發出了I/O要求,線程就將處于阻塞狀態,假設sleep()履行的睡眠結束、join()履行的等待中斷超時、I/O要求結束,則將重新回到可履行狀態,等待分配CPU。
l 同步鎖(locked):假設1個線程準備調用1個同步方法,而同步方法對應的對象正被其他線程占用,此時線程就將進入同步鎖狀態。實際上,Java中的每一個object對象都有1個monitor,此monitor負責對同步域在并發時的獨占處理,即1個線程調用某對象的同步方法時,JVM將檢測改對象的monitor是不是已被占用,如果沒有被占用,線程則得到monitor占有權,繼續履行該對象的同步方法,否則線程將被扔進1個等待線程隊列排隊,直到monitor被釋放后,所有等待的線程繼續競爭monitor占有權,搶到monitor占有權后才進入可履行狀態等待CPU的分配,才有資格履行同步方法。
l 等待(waiting):運行中的線程履行了wait()方法后就進入等待狀態。1個對象履行了wait()方法一樣將使線程進入該對象的等待線程隊列,同時它還將釋放對象鎖,即放棄monitor的占有權。只有在其他線程中對該對象調用notify()、notifyAll()方法時才會喚醒等待線程隊列中的線程,notify是隨機喚醒等待隊列中的1個線程,而nofityAll則是喚醒所有等待隊列中的線程,所有線程被喚醒后將對該對象的monitor占有權競爭,獲得到占有權的線程才能轉化為可履行狀態,等待分配CPU往下履行,其他線程則繼續等待。
圖2⑸⑺⑵