Java中線程的狀態

線程在一定條件下,狀態會發生變化。線程一共有以下幾種狀態:
1、新建狀態(New):新創建了一個線程對象。
2、就緒狀態(Runnable):線程對象創建后,其他線程調用了該對象的start()方法。該狀態的線程位于“可運行線程池”中,變得可運行,只等待獲取CPU的使用權即在就緒狀態的進程除CPU之外,其它的運行所需資源都已全部獲得。
3、運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序代碼。
4、阻塞狀態(Blocked):阻塞狀態是線程因為某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,才有機會轉到運行狀態。
阻塞的情況分三種:

  • (1)、等待阻塞:運行的線程執行wait()方法,該線程會釋放占用的所有資源,JVM會把該線程放入“等待池”中。進入這個狀態后,是不能自動喚醒的,必須依靠其他線程調用notify()或notifyAll()方法才能被喚醒,
  • (2)、同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程占用,則JVM會把該線程放入“鎖池”中。
  • (3)、其他阻塞:運行的線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置為阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。

5、死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命周期。
線程變化的狀態轉換圖如下:


注:拿到對象的鎖標記,即為獲得了對該對象(臨界區)的使用權限。即該線程獲得了運行所需的資源,進入“就緒狀態”,只需獲得CPU,就可以運行。因為當調用wait()后,線程會釋放掉它所占有的“鎖標志”,所以線程只有在此獲取資源才能進入就緒狀態。
下面小小的作下解釋:

  • 1、線程的實現有兩種方式,一是繼承Thread類,二是實現Runnable接口,但不管怎樣, 當我們new了這個對象后,線程就進入了初始狀態;
  • 2、當該對象調用了start()方法,就進入就緒狀態;
  • 3、進入就緒后,當該對象被操作系統選中,獲得CPU時間片就會進入運行狀態;
  • 4、進入運行狀態后情況就比較復雜了
    • 4.1、run()方法或main()方法結束后,線程就進入終止狀態;
    • 4.2、當線程調用了自身的sleep()方法或其他線程的join()方法,進程讓出CPU,然后就會進入阻塞狀態(該狀態既停止當前線程,但并不釋放所占有的資源****即調用sleep ()函數后,線程不會釋放它的“鎖標志”。)。當sleep()結束或join()結束后,該線程進入可運行狀態,繼續等待OS分配CPU時間片。典型地,****sleep()被用在等待某個資源就緒的情形:測試發現條件不滿足后,讓線程阻塞一段時間后重新測試,直到條件滿足為止。
    • 4.3、線程調用了yield()方法,意思是放棄當前獲得的CPU時間片,回到就緒狀態,這時與其他進程處于同等競爭狀態,OS有可能會接著又讓這個進程進入運行狀態; 調用 yield() 的效果等價于調度程序認為該線程已執行了足夠的時間片從而需要轉到另一個線程。yield()只是使當前線程重新回到可執行狀態,所以執行yield()的線程有可能在進入到可執行狀態后馬上又被執行。
    • 4.4、當線程剛進入可運行狀態(注意,還沒運行),發現將要調用的資源被synchroniza(同步),獲取不到鎖標記,將會立即進入鎖池狀態,等待獲取鎖標記(這時的鎖池里也許已經有了其他線程在等待獲取鎖標記,這時它們處于隊列狀態,既先到先得),一旦線程獲得鎖標記后,就轉入就緒狀態,等待OS分配CPU時間片;
    • 4.5、suspend() 和 resume()方法:兩個方法配套使用,suspend()使得線程進入阻塞狀態,并且不會自動恢復,必須其對應的resume()被調用,才能使得線程重新進入可執行狀態。典型地,****suspend()和 resume() 被用在等待另一個線程產生的結果的情形:**測試發現結果還沒有產生后,讓線程阻塞,另一個線程產生了結果后,調用 resume()使其恢復。
    • ** 4.6****、wait()和 notify() 方法:當線程調用wait()方法后會進入等待隊列(進入這個狀態會釋放所占有的所有資源,與阻塞狀態不同**),進入這個狀態后,是不能自動喚醒的,必須依靠其他線程調用notify()或notifyAll()方法才能被喚醒(由于notify()只是喚醒一個線程,但我們由不能確定具體喚醒的是哪一個線程,也許我們需要喚醒的線程不能夠被喚醒,因此在實際使用時,一般都用notifyAll()方法,喚醒有所線程),線程被喚醒后會進入鎖池,等待獲取鎖標記。

wait() 使得線程進入阻塞狀態,它有兩種形式:

  • 一種允許指定以毫秒為單位的一段時間作為參數;
  • 另一種沒有參數。

前者當對應的 notify()被調用或者超出指定時間時線程重新進入可執行狀態即就緒狀態,后者則必須對應的 notify()被調用。當調用wait()后,線程會釋放掉它所占有的“鎖標志”從而使線程所在對象中的其它synchronized數據可被別的線程使用。waite()和notify()因為會對對象的“鎖標志”進行操作,所以它們必須在synchronized函數或synchronizedblock中進行調用。如果在non-synchronized函數或non-synchronizedblock中進行調用,雖然能編譯通過,但在運行時會發生IllegalMonitorStateException的異常。

注意區別:初看起來wait() 和 notify() 方法與suspend()和 resume() 方法對沒有什么分別,但是事實上它們是截然不同的。區別的核心在于,前面敘述的suspend()及其它所有方法在線程阻塞時都不會釋放占用的鎖(如果占用了的話),而wait() 和 notify() 這一對方法則相反。

上述的核心區別導致了一系列的細節上的區別

  • 首先,前面敘述的所有方法都隸屬于 Thread類,但是wait() 和 notify() 方法這一對卻直接隸屬于 Object 類也就是說,所有對象都擁有這一對方法。初看起來這十分不可思議,但是實際上卻是很自然的,因為這一對方法阻塞時要釋放占用的鎖,而鎖是任何對象都具有的,調用任意對象的 wait() 方法導致線程阻塞,并且該對象上的鎖被釋放。而調用任意對象的notify()方法則導致因調用該對象的 wait()方法而阻塞的線程中隨機選擇的一個解除阻塞(但要等到獲得鎖后才真正可執行)。
  • 其次,前面敘述的所有方法都可在任何位置調用,但是wait() 和 notify() 方法這一對方法卻必須在 synchronized 方法或塊中調用,理由也很簡單,只有在synchronized方法或塊中當前線程才占有鎖,才有鎖可以釋放。同樣的道理,調用這一對方法的對象上的鎖必須為當前線程所擁有,這樣才有鎖可以釋放。因此,這一對方法調用必須放置在這樣的 synchronized方法或塊中,該方法或塊的上鎖對象就是調用這一對方法的對象。若不滿足這一條件,則程序雖然仍能編譯,但在運行時會出現IllegalMonitorStateException異常。
  • wait() 和 notify()方法的上述特性決定了它們經常和synchronized方法或塊一起使用,將它們和操作系統的進程間通信機制作一個比較就會發現它們的相似性:synchronized方法或塊提供了類似于操作系統原語的功能,它們的執行不會受到多線程機制的干擾,而這一對方法則相當于 block和wake up 原語(這一對方法均聲明為 synchronized)。它們的結合使得我們可以實現操作系統上一系列精妙的進程間通信的算法(如信號量算法),并用于解決各種復雜的線程間通信問題。

關于 wait() 和 notify() 方法最后再說明兩點:

  • 第一:調用notify() 方法導致解除阻塞的線程是從因調用該對象的 wait()方法而阻塞的線程中隨機選取的,我們無法預料哪一個線程將會被選擇,所以編程時要特別小心,避免因這種不確定性而產生問題。
  • 第二:除了notify(),還有一個方法 notifyAll()也可起到類似作用,唯一的區別在于,調用 notifyAll()方法將把因調用該對象的 wait()方法而阻塞的所有線程一次性全部解除阻塞。當然,只有獲得鎖的那一個線程才能進入可執行狀態。

談到阻塞,就不能不談一談死鎖,略一分析就能發現,suspend()方法和不指定超時期限的wait()方法的調用都可能產生死鎖。遺憾的是,Java并不在語言級別上支持死鎖的避免,我們在編程中必須小心地避免死鎖

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,327評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,996評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,316評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,406評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,128評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,524評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,576評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,759評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,310評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,065評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,249評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,821評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,479評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,909評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,140評論 1 290
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,984評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,228評論 2 375

推薦閱讀更多精彩內容

  • 本文主要講了java中多線程的使用方法、線程同步、線程數據傳遞、線程狀態及相應的一些線程函數用法、概述等。 首先講...
    李欣陽閱讀 2,477評論 1 15
  • Java多線程學習 [-] 一擴展javalangThread類 二實現javalangRunnable接口 三T...
    影馳閱讀 2,978評論 1 18
  • 該文章轉自:http://blog.csdn.net/evankaka/article/details/44153...
    加來依藍閱讀 7,373評論 3 87
  • 寫在前面的話: 這篇博客是我從這里“轉載”的,為什么轉載兩個字加“”呢?因為這絕不是簡單的復制粘貼,我花了五六個小...
    SmartSean閱讀 4,772評論 12 45
  • 本文出自 Eddy Wiki ,轉載請注明出處:http://eddy.wiki/interview-java.h...
    eddy_wiki閱讀 2,204評論 0 14