線程在一定條件下,狀態會發生變化。線程一共有以下幾種狀態:
1、新建狀態(New):新創建了一個線程對象。
2、就緒狀態(Runnable):線程對象創建后,其他線程調用了該對象的start()方法。該狀態的線程位於“可運行線程池”中,變得可運行,只等待獲取CPU的使用權,
即在就緒狀態的進程除CPU之外,其它的運行所需資源都已全部獲得。
3、運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序代碼。
4、阻塞狀態(Blocked):阻塞狀態是線程因為某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,才有機會轉到運行狀態。
阻塞的情況分三種:
①.等待阻塞:運行的線程執行wait()方法,該線程會釋放占用的所有資源,JVM會把該線程放入“等待池”中。進入這個狀態后,是不能自動喚醒的,
必須依靠其他線程調用notify()或notifyAll()方法才能被喚醒,
②.同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程占用,則JVM會把該線程放入“鎖池”中。
③.其他阻塞:運行的線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置為阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時,
或者I/O處理完畢時,線程重新轉入就緒狀態。
5、死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命周期。
線程變化的狀態轉換圖如下:

PS:拿到對象的鎖標記,即為獲得了對該對象(臨界區)的使用權限。即該線程獲得了運行所需的資源,進入“就緒狀態”,只需獲得CPU,就可以運行。
因為當調用wait()后,線程會釋放掉它所占有的“鎖標志”,所以線程只有在此獲取資源才能進入就緒狀態。
下面作下解釋:
①.線程的實現有兩種方式,一是繼承Thread類,二是實現Runnable接口,但不管怎樣, 當我們new了這個對象后,線程就進入了初始狀態;
②.當該對象調用了start()方法,就進入就緒狀態;
③.進入就緒后,當該對象被操作系統選中,獲得CPU時間片就會進入運行狀態;
④.進入運行狀態后情況就比較復雜;
(1)run()方法或main()方法結束后,線程就進入終止狀態;
(2)當線程調用了自身的sleep()方法或其他線程的join()方法,進程讓出CPU,然后就會進入阻塞狀態(該狀態既停止當前線程,但並不釋放所占有的資源,
即調用sleep()函數后,線程不會釋放它的“鎖標志”。)。當sleep()結束或join()結束后,該線程進入可運行狀態,繼續等待OS分配CPU時間片;
典型地,sleep()被用在等待某個資源就緒的情形;測試發現條件不滿足后,讓線程阻塞一段時間后重新測試,直到條件滿足為止。
(3)線程調用了yield()方法,意思是放棄當前獲得的CPU時間片,回到就緒狀態,這時與其他進程處於同等競爭狀態,OS有可能會接着又讓這個進程進入運行狀態;
調用 yield() 的效果等價於調度程序認為該線程已執行了足夠的時間片從而需要轉到另一個線程。yield()只是使當前線程重新回到可執行狀態,
所以執行yield()的線程有可能在進入到可執行狀態后馬上又被執行。
(4)當線程剛進入可運行狀態(注意,還沒運行),發現將要調用的資源被synchroniza(同步),獲取不到鎖標記,將會立即進入鎖池狀態,等待獲取鎖標記
(這時的鎖池里也許已經有了其他線程在等待獲取鎖標記,這時它們處於隊列狀態,既先到先得),一旦線程獲得鎖標記后,就轉入就緒狀態,等待OS分配CPU時間片。
(5)suspend() 和 resume()方法:兩個方法配套使用,suspend()使得線程進入阻塞狀態,並且不會自動恢復,必須其對應的resume()被調用,才能使得線程重新進入可執行狀態。
典型地,suspend()和 resume() 被用在等待另一個線程產生的結果的情形:測試發現結果還沒有產生后,讓線程阻塞,另一個線程產生了結果后,調用resume()使其恢復。
(6)wait()和 notify() 方法:當線程調用wait()方法后會進入等待隊列(進入這個狀態會釋放所占有的所有資源,與阻塞狀態不同),進入這個狀態后,是不能自動喚醒的,
必須依靠其他線程調用notify()或notifyAll()方法才能被喚醒(由於notify()只是喚醒一個線程,但我們由不能確定具體喚醒的是哪一個線程,也許我們需要喚醒的線程不能夠被喚醒,
因此在實際使用時,一般都用notifyAll()方法,喚醒有所線程),線程被喚醒后會進入鎖池,等待獲取鎖標記。
wait() 使得線程進入阻塞狀態,它有兩種形式:
一種允許指定以ms為單位的時間作為參數,另一種沒有參數。前者當對應的notify()被調用或超出指定時間時線程重新進入可執行狀態即就緒狀態,后者則必須對應的notify()被調用。
當調用wait()后,線程會釋放掉它所占有的“鎖標志”,從而使線程所在對象中的其它synchronized數據可被別的線程使用。
waite()和notify()因為會對對象的“鎖標志”進行操作,所以它們必須在synchronized函數或synchronizedblock中進行調用。
如果在non-synchronized函數或non-synchronizedblock中進行調用,雖然能編譯通過,但在運行時會發生IllegalMonitorStateException的異常。
