Java中的線程的生命周期大體可分為5種狀態(tài)专筷。
新建(NEW):新創(chuàng)建了一個線程對象蔗牡。
可運行(RUNNABLE):線程對象創(chuàng)建后蛔屹,其他線程(比如main線程)調(diào)用了該對象的start()方法议街。該狀態(tài)的線程位于可運行線程池中恩掷,等待被線程調(diào)度選中,獲取cpu 的使用權(quán) 树灶。
運行(RUNNING):可運行狀態(tài)(runnable)的線程獲得了cpu 時間片(timeslice) 纤怒,執(zhí)行程序代碼。
阻塞(BLOCKED):阻塞狀態(tài)是指線程因為某種原因放棄了cpu 使用權(quán)天通,也即讓出了cpu timeslice泊窘,暫時停止運行。直到線程進入可運行(runnable)狀態(tài)像寒,才有機會再次獲得cpu timeslice 轉(zhuǎn)到運行(running)狀態(tài)烘豹。阻塞的情況分三種:
(一). 等待阻塞:運行(running)的線程執(zhí)行o.wait()方法,JVM會把該線程放入等待隊列(waitting queue)中萝映。
(二). 同步阻塞:運行(running)的線程在獲取對象的同步鎖時吴叶,若該同步鎖被別的線程占用阐虚,則JVM會把該線程放入鎖池(lock pool)中序臂。
(三). 其他阻塞:運行(running)的線程執(zhí)行Thread.sleep(long ms)或t.join()方法,或者發(fā)出了I/O請求時实束,JVM會把該線程置為阻塞狀態(tài)奥秆。當sleep()狀態(tài)超時、join()等待線程終止或者超時咸灿、或者I/O處理完畢時构订,線程重新轉(zhuǎn)入可運行(runnable)狀態(tài)。
- 死亡(DEAD):線程run()避矢、main() 方法執(zhí)行結(jié)束悼瘾,或者因異常退出了run()方法囊榜,則該線程結(jié)束生命周期。死亡的線程不可再次復生亥宿。
一.線程的狀態(tài)圖
二.初始狀態(tài)
- 實現(xiàn)Runnable接口和繼承Thread可以得到一個線程類卸勺,new一個實例出來,線程就進入了初始狀態(tài)
三.可運行狀態(tài)
- 可運行狀態(tài)只是說你資格運行烫扼,調(diào)度程序沒有挑選到你曙求,你就永遠是可運行狀態(tài)。
- 調(diào)用線程的start()方法映企,此線程進入可運行狀態(tài)悟狱。
- 當前線程sleep()方法結(jié)束,其他線程join()結(jié)束堰氓,等待用戶輸入完畢挤渐,某個線程拿到對象鎖,這些線程也將進入可運行狀態(tài)双絮。
- 當前線程時間片用完了挣菲,調(diào)用當前線程的yield()方法,當前線程進入可運行狀態(tài)掷邦。
- 鎖池里的線程拿到對象鎖后白胀,進入可運行狀態(tài)。
四.運行狀態(tài)
- 線程調(diào)度程序從可運行池中選擇一個線程作為當前線程時線程所處的狀態(tài)抚岗。這也是線程進入運行狀態(tài)的唯一一種方式或杠。
五.死亡狀態(tài)
- 當線程的run()方法完成時,或者主線程的main()方法完成時宣蔚,我們就認為它死去向抢。這個線程對象也許是活的,但是胚委,它已經(jīng)不是一個單獨執(zhí)行的線程挟鸠。線程一旦死亡,就不能復生亩冬。
- 在一個死去的線程上調(diào)用start()方法艘希,會拋出java.lang.IllegalThreadStateException異常。
六.阻塞狀態(tài)
- 當前線程T調(diào)用Thread.sleep()方法硅急,當前線程進入阻塞狀態(tài)覆享。
- 運行在當前線程里的其它線程t2調(diào)用join()方法,當前線程進入阻塞狀態(tài)营袜。
- 等待用戶輸入的時候撒顿,當前線程進入阻塞狀態(tài)。
七.等待隊列(本是Object里的方法荚板,但影響了線程)
- 調(diào)用obj的wait(), notify()方法前凤壁,必須獲得obj鎖吩屹,也就是必須寫在synchronized(obj) 代碼段內(nèi)。
- 與等待隊列相關(guān)的步驟和圖
線程1獲取對象A的鎖拧抖,正在使用對象A祟峦。
線程1調(diào)用對象A的wait()方法。
線程1釋放對象A的鎖徙鱼,并馬上進入等待隊列宅楞。
鎖池里面的對象爭搶對象A的鎖。
線程5獲得對象A的鎖袱吆,進入synchronized塊厌衙,使用對象A。
線程5調(diào)用對象A的notifyAll()方法绞绒,喚醒所有線程婶希,所有線程進入鎖池。||||| 線程5調(diào)用對象A的notify()方法蓬衡,喚醒一個線程喻杈,不知道會喚醒誰,被喚醒的那個線程進入鎖池狰晚。
notifyAll()方法所在synchronized結(jié)束筒饰,線程5釋放對象A的鎖。
-
鎖池里面的線程爭搶對象鎖壁晒,但線程1什么時候能搶到就不知道了瓷们。||||| 原本鎖池+第6步被喚醒的線程一起爭搶對象鎖。
八.鎖池狀態(tài)
- 當前線程想調(diào)用對象A的同步方法時秒咐,發(fā)現(xiàn)對象A的鎖被別的線程占有谬晕,此時當前線程進入鎖池狀態(tài)。簡言之携取,鎖池里面放的都是想爭奪對象鎖的線程攒钳。
- 當一個線程1被另外一個線程2喚醒時,1線程進入鎖池狀態(tài)雷滋,去爭奪對象鎖不撑。
- 鎖池是在同步的環(huán)境下才有的概念,一個對象對應一個鎖池惊豺。
九.幾個方法的比較
- Thread.sleep(long millis)燎孟,一定是當前線程調(diào)用此方法禽作,當前線程進入阻塞尸昧,但不釋放對象鎖,millis后線程自動蘇醒進入可運行狀態(tài)旷偿。作用:給其它線程執(zhí)行機會的最佳方式烹俗。
- Thread.yield()爆侣,一定是當前線程調(diào)用此方法,當前線程放棄獲取的cpu時間片幢妄,由運行狀態(tài)變會可運行狀態(tài)兔仰,讓OS再次選擇線程。作用:讓相同優(yōu)先級的線程輪流執(zhí)行蕉鸳,但并不保證一定會輪流執(zhí)行乎赴。實際中無法保證yield()達到讓步目的,因為讓步的線程還有可能被線程調(diào)度程序再次選中潮尝。Thread.yield()不會導致阻塞榕吼。
- t.join()/t.join(long millis),當前線程里調(diào)用其它線程1的join方法勉失,當前線程阻塞羹蚣,但不釋放對象鎖,直到線程1執(zhí)行完畢或者millis時間到乱凿,當前線程進入可運行狀態(tài)顽素。
- obj.wait(),當前線程調(diào)用對象的wait()方法徒蟆,當前線程釋放對象鎖胁出,進入等待隊列。依靠notify()/notifyAll()喚醒或者wait(long timeout)timeout時間到自動喚醒段审。
- obj.notify()喚醒在此對象監(jiān)視器上等待的單個線程划鸽,選擇是任意性的。notifyAll()喚醒在此對象監(jiān)視器上等待的所有線程戚哎。
十.兩個疑問
- 當對象鎖被某一線程釋放的一瞬間裸诽,鎖池里面的哪個線程能獲得這個鎖?隨機型凳?隊列FIFO丈冬?or sth else?
- 等待隊列里許許多多的線程都wait()在一個對象上甘畅,此時某一線程調(diào)用了對象的notify()方法埂蕊,那喚醒的到底是哪個線程?隨機疏唾?隊列FIFO蓄氧?or sth else?java文檔就簡單的寫了句:選擇是任意性的槐脏。