一、線程狀態
線程的狀態轉換是線程控制的基礎。線程狀態總的可以分爲五大狀態:分別是生、可運行、運行、等待|阻塞|睡眠、死。 簡單描述如下:
1、新狀態:線程對象已經創建,還沒有在其上調用start()方法。
2、可運行狀態:當線程有資格運行,但調度程序還沒有把它選定爲運行線程時線程所處的狀態。當start()方法調用時,線程首先進入可運行狀態,在線程運行之後或者阻塞、等待或睡眠狀態回來後,也返回到可以運行狀態。
3、運行狀態:線程調度陳旭從可運行池中選擇一個線程作爲當前線程時線程所處的狀態。這也是線程進入運行狀態的唯一一種方式。
4、等待|阻塞|睡眠狀態:這是線程有資格運行時它所處的狀態。實際上這是那個狀態可以合爲一種,
其共同點爲:線程仍舊還活着。但是當前沒有條件運行。換句話說,它是可運行的,但是如果某件事情出現,他可能返回到可運行狀態。
5、死亡狀態:當線程的run()方法完成時就可以認爲線程死去了。這個線程對象或許是活的,但是,它已經不是一個單獨可執行的線程。線程一旦死亡,就不能復生。如果在一個死去的線程上調用start()方法,會拋出java.lang.IllegalThreadStateException異常。
線程狀態轉換圖(講究着看看)
二、阻止線程執行
對於線程的阻止,考慮一下三個方面,不考慮IO阻塞的情況:
睡眠、等待
需要一個對象的鎖定而被阻塞。
1、睡眠 sleep()
Thread.sleep(long millis) 和 Thread.sleep(long millis, int nanos)靜態方法強制當前正在執行的線程睡眠(暫停執行),以"減慢線程"。當線程睡眠時,它入睡在某個地方,在甦醒之前不會返回到可運行狀態。當睡覺時間到期,則返回到可運行狀態。
線程睡眠的原因:線程執行太快,或者需要強制進入下一輪,因爲Java規範不保證合理的輪換。
睡眠的實現:調用靜態方法即可。
public void run() {
int count = 0;
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + (count++));
}
}
睡眠的位置:爲了讓其他線程有機會執行,可以將Thread.sleep()的調用放到線程run()方法之內,這樣才能保證線程執行過程中會睡眠。
例如:
package com.sample.client;
/**
*
* @author DYLAN
*/
public class Client implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
public static void main(String[] args) {
Client client = new Client();
Thread t = new Thread(client);
t.setName("阿貓");
t.start();
Thread t2 = new Thread(client);
t2.setName("阿狗");
t2.start();
}
}
阿貓:0
阿狗:0
阿貓:1
阿狗:1
阿狗:2
阿貓:2
阿貓:3
阿狗:3
阿貓:4
阿狗:4
3、Thread.yield()方法
Thread.yield()方法作用是:暫停當前正在執行的線程對象,並執行其他線程。yield應該做的是讓當前運行的線程回到可運行狀態,以允許具有相同優先級的其他線程獲得運行機會。因此,使用yield的目的是讓相同優先級的線程之間能適當的輪轉執行。但是,實際中無法保證yield達到讓步目的,因爲讓步的線程還有可能被線程調度程序再次選中。
結論:yield()從未導致線程轉到等待|睡眠|阻塞狀態。在大多數情況下,yield()將導致線程從運行狀態轉到可運行狀態,但是可能沒有效果。
4、join()方法
Thread的非靜態方法join()讓一個線程B加入到另外一個線程A的尾部。在A執行完畢之前,B不能執行。
Client client = new Client();
Thread t = new Thread(client);
t.setName("阿貓");
t.setPriority(8);
t.start();
t.join();
Thread t2 = new Thread(client);
t2.setName("阿狗");
t2.start();
線程t執行完畢後線程t2方能執行。
小結:
到目前爲止,線程離開運行狀態的3中方法:
1、調用Thread.sleep():使當前線程睡眠至少多少毫秒(儘管它可能在指定的時間之前被調度程序中斷)。
2、調用Thread.yield():不能保障太多事情,儘管通常它會讓當前運行線程回到可運行性狀態,使得有相同優先級的線程有機會執行。
3、調用join()方法:保證當前線程停止執行,直到該線程所加入的線程完成爲止,然而,如果它加入的線程沒有存活,則當前線程不需要停止。
除了以上三種方式外,還有下面幾種特殊情況可能使線程離開運行狀態:
1、線程的run()方法正常執行完畢。
2、在對象上調用wait()方法(不是在線程上調用)。
3、線程不能在對象上獲得鎖定,它正試圖運行該對象上的方法代碼。
4、線程調度程序可以決定將當前運行狀態移動到可運行狀態,以便另一個線程獲得運行機會,而不需要任何理由。