1、新狀態:線程對象已經創建,還沒有在其上調用start()方法。
2、可運行狀態:當線程有資格運行,但調度程序還沒有把它選定為運行線程時線程所處的狀態。當start()方法調用時,線程首先進入可運行狀態。在線程運行之后或者從阻塞、等待或睡眠狀態回來后,也返回到可運行狀態。
3、運行狀態:線程調度程序從可運行池中選擇一個線程作為當前線程時線程所處的狀態。這也是線程進入運行狀態的唯一一種方式。
4、等待/阻塞/睡眠狀態:這是線程有資格運行時它所處的狀態。實際上這個三狀態組合為一種,其共同點是:線程仍舊是活的,但是當前沒有條件運行。換句話說,它是可運行的,但是如果某件事件出現,他可能返回到可運行狀態。
5、死亡態:當線程的run()方法完成時就認為它死去。這個線程對象也許是活的,但是,它已經不是一個單獨執行的線程。線程一旦死亡,就不能復生。如果在一個死去的線程上調用start()方法,會拋出java.lang.IllegalThreadStateException異常。
1、睡眠
Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)靜態方法強制當前正在執行的線程休眠(暫停執行),以“減慢線程”。當線程睡眠時,它入睡在某個地方,在蘇醒之前不會返回到可運行狀態。當睡眠時間到期,則返回到可運行狀態。
線程睡眠的原因:線程執行太快,或者需要強制進入下一輪,因為Java規范不保證合理的輪換。
package thread; public class MulityT implements Runnable { private String s; private long i = 1; public MulityT(String s) { this.s = s; } @Override public void run() { // TODO Auto-generated method stub while (i < 10) { System.out.println(s + "=====" + i++); // Thread.yield(); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { MulityT m = new MulityT("a"); Thread t = new Thread(m); t.start(); MulityT m2 = new MulityT("b"); Thread t2 = new Thread(m2); t2.start(); MulityT m3 = new MulityT("c"); Thread t3 = new Thread(m3); t3.start(); } }
輸出結果:(每輪輸出后暫停一秒左右)
a=====1 b=====1 c=====1 b=====2 a=====2 c=====2 c=====3 a=====3 b=====3 a=====4 c=====4 b=====4 a=====5 c=====5 b=====5 a=====6 c=====6 b=====6 b=====7 a=====7 c=====7 b=====8 a=====8 c=====8 c=====9 a=====9 b=====9
1、線程睡眠是幫助所有線程獲得運行機會的最好方法。
2、線程睡眠到期自動蘇醒,並返回到可運行狀態,不是運行狀態。sleep()中指定的時間是線程不會運行的最短時間。因此,sleep()方法不能保證該線程睡眠到期后就開始執行。
3、sleep()是靜態方法,只能控制當前正在運行的線程。
2、線程的優先級和線程讓步yield()
線程的讓步是通過Thread.yield()來實現的。yield()方法的作用是:暫停當前正在執行的線程對象,並執行其他線程。
要理解yield(),必須了解線程的優先級的概念。線程總是存在優先級,優先級范圍在1~10之間。JVM線程調度程序是基於優先級的搶先調度機制。在大多數情況下,當前運行的線程優先級將大於或等於線程池中任何線程的優先級。但這僅僅是大多數情況。
注意:當設計多線程應用程序的時候,一定不要依賴於線程的優先級。因為線程調度優先級操作是沒有保障的,只能把線程優先級作用作為一種提高程序效率的方法,但是要保證程序不依賴這種操作。
當線程池中線程都具有相同的優先級,調度程序的JVM實現自由選擇它喜歡的線程。這時候調度程序的操作有兩種可能:一是選擇一個線程運行,直到它阻塞或者運行完成為止。二是時間分片,為池內的每個線程提供均等的運行機會。
Thread.yield()方法作用是:暫停當前正在執行的線程對象,並執行其他線程。
yield()應該做的是讓當前運行線程回到可運行狀態,以允許具有相同優先級的其他線程獲得運行機會。因此,使用yield()的目的是讓相同優先級的線程之間能適當的輪轉執行。但是,實際中無法保證yield()達到讓步目的,因為讓步的線程還有可能被線程調度程序再次選中。
package thread; public class MulityT implements Runnable { private String s; private long i = 1; public MulityT(String s) { this.s = s; } @Override public void run() { // TODO Auto-generated method stub while (i < 10) { System.out.println(s + "=====" + i++); Thread.yield(); } } public static void main(String[] args) throws InterruptedException { MulityT m = new MulityT("a"); Thread t = new Thread(m); t.start(); MulityT m2 = new MulityT("b"); Thread t2 = new Thread(m2); t2.start(); MulityT m3 = new MulityT("c"); Thread t3 = new Thread(m3); t3.start(); } }
輸出結果:
a=====1 c=====1 b=====1 c=====2 a=====2 b=====2 c=====3 a=====3 b=====3 c=====4 a=====4 b=====4 c=====5 a=====5 b=====5 c=====6 a=====6 b=====6 c=====7 b=====7 a=====7 c=====8 b=====8 c=====9 a=====8 b=====9 a=====9
結論:yield()從未導致線程轉到等待/睡眠/阻塞狀態。在大多數情況下,yield()將導致線程從運行狀態轉到可運行狀態,但有可能沒有效果。
3、join()方法
Thread的非靜態方法join()讓一個線程B“加入”到另外一個線程A的尾部。在A執行完畢之前,B不能工作。
另外,join()方法還有帶超時限制的重載版本。例如t.join(5000);則讓線程等待5000毫秒,如果超過這個時間,則停止等待,變為可運行狀態。
package thread; public class MulityT implements Runnable { private String s; private long i = 1; public MulityT(String s) { this.s = s; } @Override public void run() { // TODO Auto-generated method stub while (i < 10) { System.out.println(s + "=====" + i++); } } public static void main(String[] args) throws InterruptedException { MulityT m = new MulityT("a"); Thread t = new Thread(m); t.start(); MulityT m2 = new MulityT("b"); Thread t2 = new Thread(m2); t2.start(); t2.join(); MulityT m3 = new MulityT("c"); Thread t3 = new Thread(m3); t3.start(); } }
輸出結果:
a=====1 b=====1 a=====2 b=====2 a=====3 b=====3 a=====4 a=====5 b=====4 a=====6 b=====5 a=====7 b=====6 a=====8 b=====7 a=====9 b=====8 b=====9 c=====1 c=====2 c=====3 c=====4 c=====5 c=====6 c=====7 c=====8 c=====9
小結:
到目前位置,介紹了線程離開運行狀態的3種方法:
1、調用Thread.sleep():使當前線程睡眠至少多少毫秒(盡管它可能在指定的時間之前被中斷)。
2、調用Thread.yield():不能保障太多事情,盡管通常它會讓當前運行線程回到可運行性狀態,使得有相同優先級的線程有機會執行。
3、調用join()方法:保證當前線程停止執行,直到該線程所加入的線程完成為止。然而,如果它加入的線程沒有存活,則當前線程不需要停止。
