[Java併發程式設計]-執行緒的六種狀態及其狀態轉換
轉載請註明:http://blog.csdn.net/UniKylin/article/details/45050823
1.執行緒自身資訊
執行緒執行的過程會產生很多資訊,這些資訊都儲存在Thread類中的成員變數裡面,常見的有:
a.執行緒的ID是唯一標識getId()
b.執行緒的名稱:getName(),如果不設定執行緒名稱預設為“Thread-xx”
c.執行緒的優先順序:getPriority,執行緒優先順序從1-10,其中數字越大表示優先級別越高,同時獲得JVM排程執行的可能性越大,JDK內建了三種常見的狀態:
//最小優先順序
public final static int MIN_PRIORITY = 1 ;
//一般優先順序
public final static int NORM_PRIORITY = 5;
//最大優先順序
public final static int MAX_PRIORITY = 10;
一般不推薦設定執行緒的優先順序,如果進行設定了非法的優先順序程式就會出現IllegalArgumentException異常。
2.執行緒的幾個狀態
1.Java執行緒有六種狀態
public enum State {
//執行緒剛建立
NEW,
//在JVM中正在執行的執行緒
RUNNABLE,
//執行緒處於阻塞狀態,等待監視鎖,可以重新進行同步程式碼塊中執行
BLOCKED,
//等待狀態
WAITING,
//呼叫sleep() join() wait()方法可能導致執行緒處於等待狀態
TIMED_WAITING,
//執行緒執行完畢,已經退出
TERMINATED;
}
上面六種狀態圖如下:
2.執行緒狀態的解釋
1.當執行緒繼承Thread或者實現了Runnable建立了執行緒物件後,當new執行緒物件過後執行緒就進入了初始的狀態。
2.當執行緒物件呼叫了start()方法的時候,執行緒啟動進入可執行的狀態。
3.執行緒進入可執行狀態後,如果邏輯完成那麼執行緒將會終結,如果沒有執行完畢中間JVM分配時間片用完,將進入可執行狀態,一旦執行緒被JVM選中則立即執行。
4.執行狀態的情況比較複雜
第一:執行緒如果執行run() main()方法結束後,完成邏輯,執行緒就進入Terminated
第二:當執行緒呼叫sleep()或者join()方法就會進入Blocked狀態,但是要注意的是阻塞的執行緒是不釋放當前所佔有的系統資源,當sleep()結束或者join()等待其他執行緒來到,當前執行緒則進入Runnable狀態等待JVM分配資源。
第三:當執行緒進入Runnable狀態,但是還沒有開始執行的時候,此時發現需要的資源處於同步狀態synchronized,這個時候執行緒將會進入Time waiting,JVM會使用佇列對這些執行緒進行控制,既先進行Time waiting的執行緒會先得到JVM資源進行執行進入Waiting
第四:如果處於Runnable的執行緒呼叫yield()讓出JVM資源,那麼就會進入New狀態和其他New狀態執行緒進行競爭重新進入Runnable
第五:如果當前執行緒呼叫wait()方法,則當前執行緒進入Time waiting但是這個時候當前執行緒會釋放所佔有的JVM資源,進入這個狀態過後是不能自動喚醒的,必須呼叫notify()或者notifyAll()方法,執行緒進入Waiting。
3.案例解釋
案例:用案例解釋執行緒的六種執行狀態,其中Pig類實現Runnable介面,邏輯是列印當前執行的執行緒資訊,
每隔一秒列印一次。在Main方法中啟動十個Pig執行緒設定相應的執行緒優先級別,並且將初始的執行緒狀態
儲存到執行緒狀態陣列中,在執行的過程判斷當前執行緒狀態和初始狀態是否相同,如果不同則列印當前線
程的資訊儲存到日誌檔案中。
class Pig implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
//執行緒進行休眠一秒
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//列印當前執行執行緒資訊
System.out.println("ThreadName : " + Thread.currentThread().getName());
}
}
}
public class App {
public static void main(String[] args) throws Exception {
// 建立現成陣列
Thread[] taskArr = new Thread[10];
// 執行緒狀態陣列
Thread.State[] threadStates = new Thread.State[10];
// 設定執行緒的狀態
for (int i = 0; i < 10; i++) {
taskArr[i] = new Thread(new Pig());
// 分別設定狀態
if ((i % 3) == 0) {
taskArr[i].setPriority(Thread.NORM_PRIORITY);
} else if ((i % 3) == 1) {
taskArr[i].setPriority(Thread.MIN_PRIORITY);
} else if ((i % 3) == 2) {
taskArr[i].setPriority(Thread.MAX_PRIORITY);
}
}
// 將執行緒資訊寫入到檔案中便於分析
FileWriter fWriter = new FileWriter(".\\log.txt");
PrintWriter pWriter = new PrintWriter(fWriter);
// 迴圈遍歷獲取執行緒的資訊
for (int i = 0; i < 10; i++) {
pWriter.println("執行緒 " + i + " 狀態:" + taskArr[i].getState());
// 將當前執行緒狀態儲存到狀態陣列中
threadStates[i] = taskArr[i].getState();
}
// 啟動執行緒
for (int i = 0; i < 10; i++) {
taskArr[i].start();
}
// 在執行過程中如果執行緒的狀態和初始狀態不一樣就將狀態變化過程寫入到檔案中
boolean finish = false;
while (!finish) {
for (int i = 0; i < 10; i++) {
// 執行緒狀態發生變化
if (taskArr[i].getState() != threadStates[i]) {
// 列印執行緒當前資訊
printThreadMsg(pWriter, taskArr[i], threadStates[i]);
// 將當前執行緒狀態儲存到執行緒狀態陣列中
threadStates[i] = taskArr[i].getState();
}
}
finish = true;
for (int i = 0; i < 10; i++) {
finish = finish && (taskArr[i].getState() == State.TERMINATED);
}
}
}
/**
* 列印當前執行緒的資訊
* @param pWriter
* @param thread
* @param state
*/
private static void printThreadMsg(PrintWriter pWriter, Thread thread, State state) {
pWriter.println("*********************************************************");
pWriter.println("執行緒ID: " + thread.getId() + " 執行緒名稱:" + thread.getName());
pWriter.println("執行緒優先順序:" + thread.getPriority());
pWriter.println("執行緒過去狀態:" + state);
pWriter.println("執行緒當前狀態:" + thread.getState());
pWriter.println("*********************************************************");
}
}
部分執行結果如下:
分析上面的部分執行結果就可以看出當Pig執行緒進行休眠的時候,就會導致其他執行緒狀態的變換,其中過去狀態和當前狀態可以明顯的反應出線程狀態切換。