Java線程的狀態轉換
對於具體的幾個狀態解釋,這裏就不寫了,比較容易理解。
對於線程的阻止,考慮一下三個方面,不考慮IO阻塞的情況:
l 睡眠:
l 等待
l 因爲需要一個對象的鎖定而被阻塞。
所謂的“併發執行”、“同時”其實都不是真正意義上的“同時”。衆所周知,CPU都有個時鐘頻率,表示每秒中能執行cpu指令的次數。在每個時鐘週期內,CPU實際上只能去執行一條(也有可能多條)指令。操作系統將進程線程進行管理,輪流(沒有固定的順序)分配每個進程很短的一段是時間(不一定是均分),然後在每個線程內部,程序代碼自己處理該進程內部線程的時間分配,多個線程之間相互的切換去執行,這個切換時間也是非常短的。因此多任務、多進程、多線程都是操作系統給人的一種宏觀感受,從微觀角度看,程序的運行是異步執行的。
用一句話做總結:雖然操作系統是多線程的,但CPU每一時刻只能做一件事,和人的大腦是一樣的,呵呵。
1、睡眠
Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)靜態方法強制當前正在執行的線程休眠(暫停執行),以“減慢線程”。當線程睡眠時,它入睡在某個地方,在甦醒之前不會返回到可運行狀態。當睡眠時間到期,則返回到可運行狀態。
線程睡眠的原因:線程執行太快,或者需要強制進入下一輪,因爲Java規範不保證合理的輪換。
睡眠的實現:調用靜態方法。
- try {
- Thread.sleep(123);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
睡眠的位置:爲了讓其他線程有機會執行,可以將Thread.sleep()的調用放線程run()之內。這樣才能保證該線程執行過程中會睡眠。
注意:
1、線程睡眠是幫助所有線程獲得運行機會的最好方法。
2、線程睡眠到期自動甦醒,並返回到可運行狀態,不是運行狀態。sleep()中指定的時間是線程不會運行的最短時間。因此,sleep()方法不能保證該線程睡眠到期後就開始執行。
3、sleep()是靜態方法,只能控制當前正在運行的線程。
2、線程的優先級和線程讓步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 分配給線程的默認優先級。
3、Thread.yield()方法
Thread.yield()方法作用是:暫停當前正在執行的線程對象,並執行其他線程。
yield()應該做的是讓當前運行線程回到可運行狀態,以允許具有相同優先級的其他線程獲得運行機會。因此,使用yield()的目的是讓相 同優先級的線程之間能適當的輪轉執行。但是,實際中無法保證yield()達到讓步目的,因爲讓步的線程還有可能被線程調度程序再次選中。
結論:yield()從未導致線程轉到等待/睡眠/阻塞狀態。在大多數情況下,yield()將導致線程從運行狀態轉到可運行狀態,但有可能沒有效果。
- /**
- * Java線程:線程的調度-讓步
- *
- * @author leizhimin 2009-11-4 9:02:40
- */
- public class Test {
- public static void main(String[] args) {
- Thread t1 = new MyThread1();
- Thread t2 = new Thread(new MyRunnable());
- t2.start();
- t1.start();
- }
- }
- class MyThread1 extends Thread {
- public void run() {
- for (int i = 0; i < 10; i++) {
- System.out.println("線程1第" + i + "次執行!");
- }
- }
- }
- class MyRunnable implements Runnable {
- public void run() {
- for (int i = 0; i < 10; i++) {
- System.out.println("線程2第" + i + "次執行!");
- Thread.yield();
- }
- }
- }
4、join()方法
Thread的非靜態方法join()讓一個線程B“加入”到另外一個線程A的尾部。在A執行完畢之前,B不能工作。例如:
Thread t = new MyThread();
t.start();
t.join();
另外,join()方法還有帶超時限制的重載版本。 例如t.join(5000);則讓線程等待5000毫秒,如果超過這個時間,則停止等待,變爲可運行狀態。
線程的加入join()對線程棧導致的結果是線程棧發生了變化,當然這些變化都是瞬時的。下面給示意圖:
測試代碼:
public class Main {
public static class TestThread extends Thread {
@Override
public void run() {
int i = 0;
long time = System.currentTimeMillis();
while (i <= 10) {
while (System.currentTimeMillis() - time < 1000) {
}
time = System.currentTimeMillis();
System.out.println(i);
i++;
}
}
}
public static void main(String[] args) throws Exception {
System.out.println("start");
TestThread testThread = new TestThread();
testThread.start();
testThread.join();
System.out.println("end");
}
}
輸出結果:
小結
到目前位置,介紹了線程離開運行狀態的3種方法:
1、調用Thread.sleep():使當前線程睡眠至少多少毫秒(儘管它可能在指定的時間之前被中斷)。
2、調用Thread.yield():不能保障太多事情,儘管通常它會讓當前運行線程回到可運行性狀態,使得有相同優先級的線程有機會執行。
3、調用join()方法:保證當前線程停止執行,直到該線程所加入的線程完成爲止。然而,如果它加入的線程沒有存活,則當前線程不需要停止。
除了以上三種方式外,還有下面幾種特殊情況可能使線程離開運行狀態:
1、線程的run()方法完成。
2、在對象上調用wait()方法(不是在線程上調用)。
3、線程不能在對象上獲得鎖定,它正試圖運行該對象的方法代碼。
4、線程調度程序可以決定將當前運行狀態移動到可運行狀態,以便讓另一個線程獲得運行機會,而不需要任何理由。
相關引用:
http://wenku.baidu.com/view/07ccd32f647d27284b735148.html
http://blog.csdn.net/liu1028701143/article/details/6754590
http://blog.csdn.net/jimjarry/article/details/6754598