一、線程狀態(tài)
線程的狀態(tài)轉(zhuǎn)換是線程控制的基礎。線程狀態(tài)總的可分為五大狀態(tài):分別是新建、就緒、運行、等待/阻塞、死亡。各個狀態(tài)描述如下:
1、新建狀態(tài):線程對象已經(jīng)創(chuàng)建,還沒有在其上調(diào)用start()方法。
2、就緒狀態(tài):當線程有資格運行,但調(diào)度程序還沒有把它選定為運行線程時線程所處的狀態(tài)。當start()方法調(diào)用時,線程首先進入可運行狀態(tài)。在線程運行之后或者從阻塞、等待或睡眠狀態(tài)回來后,也返回到可運行狀態(tài)。
3、運行狀態(tài):線程調(diào)度程序從可運行池中選擇一個線程作為當前線程時線程所處的狀態(tài)。這也是線程進入運行狀態(tài)的唯一一種方式。
4、等待/阻塞/睡眠狀態(tài):這是線程有資格運行時它所處的狀態(tài)。實際上這個三狀態(tài)組合為一種,其共同點是:線程仍舊是活的,但是當前沒有條件運行。換句話說,它是可運行的,但是如果某件事件出現(xiàn),他可能返回到可運行狀態(tài)。
5、死亡狀態(tài):當線程的run()方法完成時就認為它死去。這個線程對象也許是活的,但是,它已經(jīng)不是一個單獨執(zhí)行的線程。線程一旦死亡,就不能復生。如果在一個死去的線程上調(diào)用start()方法,會拋出java.lang.IllegalThreadStateException異常。 二、阻止線程執(zhí)行 對于線程的阻止,考慮一下三個方面,不考慮IO阻塞的情況: 睡眠; 等待; 因為需要一個對象的鎖定而被阻塞。
1、睡眠 Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)靜態(tài)方法強制當前正在執(zhí)行的線程休眠(暫停執(zhí)行),以“減慢線程”。當線程睡眠時,它入睡在某個地方,在蘇醒之前不會返回到可運行狀態(tài)。當睡眠時間到期,則返回到可運行狀態(tài)。
線程睡眠的原因:線程執(zhí)行太快,或者需要強制進入下一輪,因為Java規(guī)范不保證合理的輪換。
睡眠的實現(xiàn):調(diào)用靜態(tài)方法。 try {
睡眠的位置:為了讓其他線程有機會執(zhí)行,可以將Thread.sleep()的調(diào)用放線程run()之內(nèi)。這樣才能保證該線程執(zhí)行過程中會睡眠。
例如,在前面的例子中,將一個耗時的操作改為睡眠,以減慢線程的執(zhí)行??梢赃@么寫:
public void run() { // 很耗時的操作,用來減慢線程的執(zhí)行
運行結(jié)果: 阿三 :0
這樣,線程在每次執(zhí)行過程中,總會睡眠3毫秒,睡眠了,其他的線程就有機會執(zhí)行了。
注意: 1、線程睡眠是幫助所有線程獲得運行機會的最好方法。 2、線程睡眠到期自動蘇醒,并返回到可運行狀態(tài),不是運行狀態(tài)。sleep()中指定的時間是線程不會運行的最短時間。因此,sleep()方法不能保證該線程睡眠到期后就開始執(zhí)行。 3、sleep()是靜態(tài)方法,只能控制當前正在運行的線程。
下面給個例子: /**
-------0
線程的讓步是通過Thread.yield()來實現(xiàn)的。yield()方法的作用是:暫停當前正在執(zhí)行的線程對象,并執(zhí)行其他線程。
要理解yield(),必須了解線程的優(yōu)先級的概念。線程總是存在優(yōu)先級,優(yōu)先級范圍在1~10之間。JVM線程調(diào)度程序是基于優(yōu)先級的搶先調(diào)度機制。在大多數(shù)情況下,當前運行的線程優(yōu)先級將大于或等于線程池中任何線程的優(yōu)先級。但這僅僅是大多數(shù)情況。
注意:當設計多線程應用程序的時候,一定不要依賴于線程的優(yōu)先級。因為線程調(diào)度優(yōu)先級操作是沒有保障的,只能把線程優(yōu)先級作用作為一種提高程序效率的方法,但是要保證程序不依賴這種操作。
當線程池中線程都具有相同的優(yōu)先級,調(diào)度程序的JVM實現(xiàn)自由選擇它喜歡的線程。這時候調(diào)度程序的操作有兩種可能:一是選擇一個線程運行,直到它阻塞或者運行完成為止。二是時間分片,為池內(nèi)的每個線程提供均等的運行機會。
設置線程的優(yōu)先級:線程默認的優(yōu)先級是創(chuàng)建它的執(zhí)行線程的優(yōu)先級??梢酝ㄟ^setPriority(int newPriority)更改線程的優(yōu)先級。例如: Thread t = new MyThread(); 線程優(yōu)先級為1~10之間的正整數(shù),JVM從不會改變一個線程的優(yōu)先級。然而,1~10之間的值是沒有保證的。一些JVM可能不能識別10個不同的值,而將這些優(yōu)先級進行每兩個或多個合并,變成少于10個的優(yōu)先級,則兩個或多個優(yōu)先級的線程可能被映射為一個優(yōu)先級。
線程默認優(yōu)先級是5,Thread類中有三個常量,定義線程優(yōu)先級范圍: static int MAX_PRIORITY
3、Thread.yield()方法
Thread.yield()方法作用是:暫停當前正在執(zhí)行的線程對象,并執(zhí)行其他線程。 yield()應該做的是讓當前運行線程回到可運行狀態(tài),以允許具有相同優(yōu)先級的其他線程獲得運行機會。因此,使用yield()的目的是讓相同優(yōu)先級的線程之間能適當?shù)妮嗈D(zhuǎn)執(zhí)行。但是,實際中無法保證yield()達到讓步目的,因為讓步的線程還有可能被線程調(diào)度程序再次選中。 結(jié)論:yield()從未導致線程轉(zhuǎn)到等待/睡眠/阻塞狀態(tài)。在大多數(shù)情況下,yield()將導致線程從運行狀態(tài)轉(zhuǎn)到可運行狀態(tài),但有可能沒有效果。
4、join()方法
Thread的非靜態(tài)方法join()讓一個線程B“加入”到另外一個線程A的尾部。在A執(zhí)行完畢之前,B不能工作。例如: Thread t = new MyThread(); 另外,join()方法還有帶超時限制的重載版本。例如t.join(5000);則讓線程等待5000毫秒,如果超過這個時間,則停止等待,變?yōu)榭蛇\行狀態(tài)。
線程的加入join()對線程棧導致的結(jié)果是線程棧發(fā)生了變化,當然這些變化都是瞬時的。下面給示意圖:
小結(jié) 到目前位置,介紹了線程離開運行狀態(tài)的3種方法: 1、調(diào)用Thread.sleep():使當前線程睡眠至少多少毫秒(盡管它可能在指定的時間之前被中斷)。 2、調(diào)用Thread.yield():不能保障太多事情,盡管通常它會讓當前運行線程回到可運行性狀態(tài),使得有相同優(yōu)先級的線程有機會執(zhí)行。 3、調(diào)用join()方法:保證當前線程停止執(zhí)行,直到該線程所加入的線程完成為止。然而,如果它加入的線程沒有存活,則當前線程不需要停止。
除了以上三種方式外,還有下面幾種特殊情況可能使線程離開運行狀態(tài): 1、線程的run()方法完成。 2、在對象上調(diào)用wait()方法(不是在線程上調(diào)用)。 3、線程不能在對象上獲得鎖定,它正試圖運行該對象的方法代碼。 4、線程調(diào)度程序可以決定將當前運行狀態(tài)移動到可運行狀態(tài),以便讓另一個線程獲得運行機會,而不需要任何理由。 |
|
來自: Lib4Kevin > 《JAVA線程由入門到精通》