1.線程自身信息
線程運行的過程會產生很多信息,這些信息都保存在Thread類中的成員變量裏面,常見的有:
- 線程的ID是唯一標識getId()
- 線程的名稱:getName(),如果不設置線程名稱默認爲“Thread-xx”
- 線程的優先級: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線程設置相應的線程優先級別,並且將初始的線程狀態
保存到線程狀態數組中,在運行的過程判斷當前線程狀態和初始狀態是否相同,如果不同則打印當前線
程的信息保存到日誌文件中。
import java.io.FileWriter;
import java.io.PrintWriter;
import java.lang.Thread.State;
import java.util.concurrent.TimeUnit;
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("F:/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線程進行休眠的時候,就會導致其他線程狀態的變換,其中過去狀態和當前狀態可以明顯的反應出線程狀態切換。