線程的生命週期及五種基本狀態
關於Java中線程的生命週期,首先看一下下面這張較爲經典的圖:
- 新建(new Thread)
當創建Thread類的一個實例(對象)時,此線程進入新建狀態(未被啓動)。例如:Thread t1=new Thread();
- 就緒(runnable)
調用Thread類的start方法,線程已經被啓動,進入就緒狀態,正在等待被分配給CPU時間片,也就是說此時線程正在就緒隊列中排隊等候得到CPU資源。
- 運行(running)
線程獲得CPU資源正在執行任務(執行run()方法),此時除非此線程自動放棄CPU資源或者有優先級更高的線程進入,線程將一直運行到結束或者時間片結束。
- 堵塞(blocked)
由於某種原因導致正在運行的線程讓出CPU並暫停自己的執行,即進入堵塞狀態。阻塞結束後線程進入就緒狀態。
堵塞的情況分三種:(一)等待堵塞:執行的線程執行wait()方法,JVM會把該線程放入等待池中。
(二)同步堵塞:執行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用。則JVM會把該線程放入鎖池中。
(三)其它堵塞:執行的線程執行sleep()或join()方法,或者發出了I/O請求時。JVM會把該線程置爲堵塞狀態。
當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完成時。線程又一次轉入就緒狀態。
-
死亡(dead)
當線程執行完畢(run方法運行結束)或被其它線程殺死,線程就進入死亡狀態,這時線程不可能再進入就緒狀態等待執行。
線程間的狀態轉換
- 解釋:
1、線程的實現有兩種方式,一是繼承Thread類,二是實現Runnable接口,但無論如何,當我們new了這個對象後。線程就進入了初始狀態;
2、當該對象調用了start()方法,就進入可執行狀態; 3、進入可執行狀態後,當該對象被操作系統選中。獲得CPU時間片就會進入執行狀態;
4、進入執行狀態後情況就比較複雜了
4.1、run()方法或main()方法結束後,線程就進入終止狀態;
4.2、當線程調用了自身的sleep()方法或其它線程的join()方法,就會進入堵塞狀態(該狀態既停止當前線程,但並不釋放所佔有的資源)。當sleep()結束或join()結束後。該線程進入可執行狀態,繼續等待OS分配時間片;
4.3、線程調用了yield()方法,意思是放棄當前獲得的CPU時間片,回到可執行狀態,這時與其它進程處於同等競爭狀態,OS有可能會接着又讓這個進程進入執行狀態。4.4、當線程剛進入可執行狀態(注意,還沒執行),發現將要調用的資源被synchroniza(同步),獲取不到鎖標記。將會馬上進入鎖池狀態,等待獲取鎖標記(這時的鎖池裏或許已經有了其它線程在等待獲取鎖標記,這時它們處於隊列狀態,既先到先得),一旦線程獲得鎖標記後,就轉入可執行狀態。等待OS分配CPU時間片。
4.5、當線程調用wait()方法後會進入等待隊列(進入這個狀態會釋放所佔有的全部資源,與堵塞狀態不同)。進入這個狀態後。是不能自己主動喚醒的,必須依靠其它線程調用notify()或notifyAll()方法才幹被喚醒(因爲notify()僅僅是喚醒一個線程,但我們由不能確定詳細喚醒的是哪一個線程。或許我們須要喚醒的線程不可以被喚醒,因此在實際使用時,一般都用notifyAll()方法,喚醒有所線程),線程被喚醒後會進入鎖池。等待獲取鎖標記。
一個線程會因爲以下原因而放棄CPU:
-
時間片用完了,java虛擬機讓當前線程暫時放棄CPU,轉到就緒狀態,使其它線程獲得運行機會。
-
當前線程因爲某些原因而進入阻塞狀態
-
線程結束運行
線程在以下情況會停止:
run方法正常執行完畢
run方法執行過程中拋出一個未捕獲的異常
調用stop方法(不推薦使用)
進程的停止,當一個進程中所有的前臺線程停止後,該進程結束。
如果希望明確地讓一個線程給另外一個線程運行的機會,可以採取以下辦法。
1. 調整各個線程的優先級
2. 讓處於運行狀態的線程調用Thread.sleep()方法
3. 讓處於運行狀態的線程調用Thread.yield()方法
4. 讓處於運行狀態的線程調用另一個線程的join()方法