Java線程:線程狀態的轉換

Java線程:線程狀態的轉換
 
SCJP5學習筆記
 
1、線程狀態
 
線程的狀態轉換是線程控制的基礎。線程狀態總的可分爲五大狀態:分別是生、死、可運行、運行、等待/阻塞。用一個圖來描述以下:
 
一、新狀態:線程對象已經建立,尚未在其上調用start()方法。
 
二、可運行狀態:當線程有資格運行,但調度程序尚未把它選定爲運行線程時線程所處的狀態。當start()方法調用時,線程首先進入可運行狀態。在線程運行以後或者從阻塞、等待或睡眠狀態回來後,也返回到可運行狀態。
 
三、運行狀態:線程調度程序從可運行池中選擇一個線程做爲當前線程時線程所處的狀態。這也是線程進入運行狀態的惟一一種方式。
 
四、等待/阻塞/睡眠狀態:這是線程有資格運行時它所處的狀態。實際上這個三狀態組合爲一種,其共同點是:線程仍舊是活的,可是當前沒有條件運行。換句話說,它是可運行的,可是若是某件事件出現,他可能返回到可運行狀態。
 
五、死亡態:當線程的run()方法完成時就認爲它死去。這個線程對象也許是活的,可是,它已經不是一個單獨執行的線程。線程一旦死亡,就不能復生。 若是在一個死去的線程上調用start()方法,會拋出java.lang.IllegalThreadStateException異常。
 
有關詳細狀態轉換圖能夠參看本人的「 Java多線程編程總結」中的圖
 
2、阻止線程執行
對於線程的阻止,考慮一下三個方面,不考慮IO阻塞的狀況:
睡眠;
等待;
由於須要一個對象的鎖定而被阻塞。
 
一、睡眠
Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)靜態方法強制當前正在執行的線程休眠(暫停執行),以「減慢線程」。當線程睡眠時,它入睡在某個地方,在甦醒以前不會返回到可運行狀態。當睡眠時間到期,則返回到可運行狀態。
 
線程睡眠的緣由:線程執行太快,或者須要強制進入下一輪,由於Java規範不保證合理的輪換。
 
睡眠的實現:調用靜態方法。
        try {
            Thread.sleep(123);
        } catch (InterruptedException e) {
            e.printStackTrace(); 
        }
 
睡眠的位置:爲了讓其餘線程有機會執行,能夠將Thread.sleep()的調用放線程run()以內。這樣才能保證該線程執行過程當中會睡眠。
 
例如,在前面的例子中,將一個耗時的操做改成睡眠,以減慢線程的執行。能夠這麼寫:
 
    public void run() {
        for(int i = 0;i<5;i++){
// 很耗時的操做,用來減慢線程的執行
//            for(long k= 0; k <100000000;k++);
            try {
                Thread.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();  .
            }

            System.out.println(this.getName()+" :"+i);
        }
    }
 
運行結果:
阿三 :0
李四 :0
阿三 :1
阿三 :2
阿三 :3
李四 :1
李四 :2
阿三 :4
李四 :3
李四 :4

Process finished with exit code 0
 
這樣,線程在每次執行過程當中,總會睡眠3毫秒,睡眠了,其餘的線程就有機會執行了。
 
注意:
一、線程睡眠是幫助全部線程得到運行機會的最好方法。
二、線程睡眠到期自動甦醒,並返回到可運行狀態,不是運行狀態。sleep()中指定的時間是線程不會運行的最短期。所以,sleep()方法不能保證該線程睡眠到期後就開始執行。
三、sleep()是靜態方法,只能控制當前正在運行的線程。
 
下面給個例子:
/**
* 一個計數器,計數到100,在每一個數字之間暫停1秒,每隔10個數字輸出一個字符串
*
* @author leizhimin 2008-9-14 9:53:49
*/

public class MyThread extends Thread {

     public void run() {
         for ( int i = 0; i < 100; i++) {
             if ((i) % 10 == 0) {
                System.out.println( "-------" + i);
            }
            System.out.print(i);
             try {
                Thread.sleep(1);
                System.out.print( "    線程睡眠1毫秒!\n");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

     public static void main(String[] args) {
         new MyThread().start();
    }
}
 
-------0
0    線程睡眠1毫秒!
1    線程睡眠1毫秒!
2    線程睡眠1毫秒!
3    線程睡眠1毫秒!
4    線程睡眠1毫秒!
5    線程睡眠1毫秒!
6    線程睡眠1毫秒!
7    線程睡眠1毫秒!
8    線程睡眠1毫秒!
9    線程睡眠1毫秒!
-------10
10    線程睡眠1毫秒!
11    線程睡眠1毫秒!
12    線程睡眠1毫秒!
13    線程睡眠1毫秒!
14    線程睡眠1毫秒!
15    線程睡眠1毫秒!
16    線程睡眠1毫秒!
17    線程睡眠1毫秒!
18    線程睡眠1毫秒!
19    線程睡眠1毫秒!
-------20
20    線程睡眠1毫秒!
21    線程睡眠1毫秒!
22    線程睡眠1毫秒!
23    線程睡眠1毫秒!
24    線程睡眠1毫秒!
25    線程睡眠1毫秒!
26    線程睡眠1毫秒!
27    線程睡眠1毫秒!
28    線程睡眠1毫秒!
29    線程睡眠1毫秒!
-------30
30    線程睡眠1毫秒!
31    線程睡眠1毫秒!
32    線程睡眠1毫秒!
33    線程睡眠1毫秒!
34    線程睡眠1毫秒!
35    線程睡眠1毫秒!
36    線程睡眠1毫秒!
37    線程睡眠1毫秒!
38    線程睡眠1毫秒!
39    線程睡眠1毫秒!
-------40
40    線程睡眠1毫秒!
41    線程睡眠1毫秒!
42    線程睡眠1毫秒!
43    線程睡眠1毫秒!
44    線程睡眠1毫秒!
45    線程睡眠1毫秒!
46    線程睡眠1毫秒!
47    線程睡眠1毫秒!
48    線程睡眠1毫秒!
49    線程睡眠1毫秒!
-------50
50    線程睡眠1毫秒!
51    線程睡眠1毫秒!
52    線程睡眠1毫秒!
53    線程睡眠1毫秒!
54    線程睡眠1毫秒!
55    線程睡眠1毫秒!
56    線程睡眠1毫秒!
57    線程睡眠1毫秒!
58    線程睡眠1毫秒!
59    線程睡眠1毫秒!
-------60
60    線程睡眠1毫秒!
61    線程睡眠1毫秒!
62    線程睡眠1毫秒!
63    線程睡眠1毫秒!
64    線程睡眠1毫秒!
65    線程睡眠1毫秒!
66    線程睡眠1毫秒!
67    線程睡眠1毫秒!
68    線程睡眠1毫秒!
69    線程睡眠1毫秒!
-------70
70    線程睡眠1毫秒!
71    線程睡眠1毫秒!
72    線程睡眠1毫秒!
73    線程睡眠1毫秒!
74    線程睡眠1毫秒!
75    線程睡眠1毫秒!
76    線程睡眠1毫秒!
77    線程睡眠1毫秒!
78    線程睡眠1毫秒!
79    線程睡眠1毫秒!
-------80
80    線程睡眠1毫秒!
81    線程睡眠1毫秒!
82    線程睡眠1毫秒!
83    線程睡眠1毫秒!
84    線程睡眠1毫秒!
85    線程睡眠1毫秒!
86    線程睡眠1毫秒!
87    線程睡眠1毫秒!
88    線程睡眠1毫秒!
89    線程睡眠1毫秒!
-------90
90    線程睡眠1毫秒!
91    線程睡眠1毫秒!
92    線程睡眠1毫秒!
93    線程睡眠1毫秒!
94    線程睡眠1毫秒!
95    線程睡眠1毫秒!
96    線程睡眠1毫秒!
97    線程睡眠1毫秒!
98    線程睡眠1毫秒!
99    線程睡眠1毫秒!

Process finished with exit code 0

二、線程的優先級和線程讓步yield()
線程的讓步是經過Thread. yield()來實現的。yield()方法的做用是:暫停當前正在執行的線程對象,並執行其餘線程。
 
要理解yield(),必須瞭解線程的優先級的概念。線程老是存在優先級,優先級範圍在1~10之間。JVM線程調度程序是基於優先級的搶先調度機制。在大多數狀況下,當前運行的線程優先級將大於或等於線程池中任何線程的優先級。但這僅僅是大多數狀況。
 
注意:當設計多線程應用程序的時候,必定不要依賴於線程的優先級。由於線程調度優先級操做是沒有保障的,只能把線程優先級做用做爲一種提升程序效率的方法,可是要保證程序不依賴這種操做。
 
當線程池中線程都具備相同的優先級,調度程序的JVM實現自由選擇它喜歡的線程。這時候調度程序的操做有兩種可能:一是選擇一個線程運行,直到它阻塞或者運行完成爲止。二是時間分片,爲池內的每一個線程提供均等的運行機會。
 
設置線程的優先級:線程默認的優先級是建立它的執行線程的優先級。能夠經過setPriority(int newPriority)更改線程的優先級。例如:
        Thread t = new MyThread();
        t.setPriority(8);
        t.start();
線程優先級爲1~10之間的正整數,JVM從不會改變一個線程的優先級。然而,1~10之間的值是沒有保證的。一些JVM可能不能識別10個不一樣的值,而將這些優先級進行每兩個或多個合併,變成少於10個的優先級,則兩個或多個優先級的線程可能被映射爲一個優先級。
 
線程默認優先級是5,Thread類中有三個常量,定義線程優先級範圍:
static int MAX_PRIORITY
          線程能夠具備的最高優先級。
static int MIN_PRIORITY
          線程能夠具備的最低優先級。
static int NORM_PRIORITY
          分配給線程的默認優先級。
 
三、Thread.yield()方法
 
Thread.yield()方法做用是:暫停當前正在執行的線程對象,並執行其餘線程。
yield()應該作的是讓當前運行線程回到可運行狀態,以容許具備相同優先級的其餘線程得到運行機會。所以,使用yield()的目的是讓相同優先級的線程之間能適當的輪轉執行。可是,實際中沒法保證yield()達到讓步目的,由於讓步的線程還有可能被線程調度程序再次選中。
結論:yield()從未致使線程轉到等待/睡眠/阻塞狀態。在大多數狀況下,yield()將致使線程從運行狀態轉到可運行狀態,但有可能沒有效果。
 
四、join()方法
 
Thread的非靜態方法join()讓一個線程B「加入」到另一個線程A的尾部。在A執行完畢以前,B不能工做。例如:
        Thread t = new MyThread();
        t.start();
        t.join();
另外,join()方法還有帶超時限制的重載版本。 例如t.join(5000);則讓線程等待5000毫秒,若是超過這個時間,則中止等待,變爲可運行狀態。
 
線程的加入join()對線程棧致使的結果是線程棧發生了變化,固然這些變化都是瞬時的。下面給示意圖:
 
 
 
小結
到目前位置,介紹了線程離開運行狀態的3種方法:
一、調用Thread.sleep():使當前線程睡眠至少多少毫秒(儘管它可能在指定的時間以前被中斷)。
二、調用Thread.yield():不能保障太多事情,儘管一般它會讓當前運行線程回到可運行性狀態,使得有相同優先級的線程有機會執行。
三、調用join()方法:保證當前線程中止執行,直到該線程所加入的線程完成爲止。然而,若是它加入的線程沒有存活,則當前線程不須要中止。
 
除了以上三種方式外,還有下面幾種特殊狀況可能使線程離開運行狀態:
一、線程的run()方法完成。
二、在對象上調用wait()方法(不是在線程上調用)。
三、線程不能在對象上得到鎖定,它正試圖運行該對象的方法代碼。
四、線程調度程序能夠決定將當前運行狀態移動到可運行狀態,以便讓另外一個線程得到運行機會,而不須要任何理由。
相關文章
相關標籤/搜索