Thread類包含幾個屬性,這些屬性所表示的信息能幫助我們識別線程、觀察其狀態、控制其優先級等。這些線程包括如下幾種:
ID: 該屬性表示每個線程的唯一標識;
Name: 該屬性存儲每個線程的名稱;
Priority: 該屬性存儲每個Thread對象的優先級。線程優先級分1到10十個級別,1表示最低優先級,10表示最高優先級。并不推薦修改線程的優先級,但是如果確實有這方面的需求,也可以嘗試一下。
Status: 該屬性存儲線程的狀態。線程共有六種不同的狀態:新建(new)、運行(runnable)、阻塞(blocked)、等待(waiting)、限時等待(time waiting)或者終止(terminated)。線程的狀態必定是其中一種。
在本小節,我們將開發一個程序,程序中新建十個線程,并且設定每個線程的名稱和優先級。然后執行線程,觀察線程的狀態信息,直到線程執行結束。再說明一點,這些線程還是計算一個數的乘法表。
知其然
按照下面所示步驟,來實現該示例:
1.創建一個名為 Calculator的類,實現Runnable接口。代碼如下:
public class Calculator implements Runnable {
2.聲明一個私有的整形屬性,名稱為number,實現該類的構造函數來初始化剛剛聲明的屬性。代碼如下:
private int number;
public Calculator(int number) {
this.number = number;
}
3.實現run()方法,該方法是我們創建的線程執行時運行的程序(instruction),故而該方法用于計算乘法表。具體代碼如下:
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.printf("%s: %d * %d = %d\n",
Thread.currentThread().getName(),
number, i, i * number);
}
}
4.現在,我們來實現示例應用的主類(main class)。創建名為Main的類,在該類中添加main方法。代碼如下:
public class Main {
public static void main(String[] args) {
5.創建兩個包含十個元素數組,一個是Thread類型的,一個是Thread.State類型,然后全部初始化。這兩個數組,一個用于存儲我們將以執行的線程,另外一個存儲這些線程的狀態。代碼如下:
Thread[] threads = new Thread[10];
Thread.State[] status = new Thread.State[threads.length];
6.創建十個Calculator對象,并且使用不同的數來初始化每個對象。使用這些Calculator對象創建十個Thread對象,存儲到上面的創建數組中。同時,設置這些線程的優先級,五個設置成最高優先級;五個設置成最低優先級。代碼如下:
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new Calculator(i));
if ((i % 2) == 0) {
threads[i].setPriority(Thread.MAX_PRIORITY);
} else {
threads[i].setPriority(Thread.MIN_PRIORITY);
}
threads[i].setName("Thread-" + i);
}
7.創建一個PrintWriter對象,用于將線程狀態的變換記錄到文件中。代碼如下:
try (FileWriter file = new FileWriter("D:\\thread.log");
PrintWriter pw = new PrintWriter(file)) {
這里使用了Java7的語法,所以請將JDK升級到第七版,把編譯工具設置成Java7。否則會報語法錯誤。
8.將所有線程的狀態寫到文件中?,F在,現在的狀態應該是新建(NEW)。代碼如下:
for (int i = 0; i < threads.length; i++) {
Thread thread = threads[i];
pw.println("Main: Status of Thread " + i +
" : " + threads[i].getState());
status[i] = threads[i].getState();
}
9.啟動所有線程。代碼入下:
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
10.另外一方面,我們一直監控線程,直到線程執行結束。如果我們檢測到線程的狀態有所改變,則立即將線程狀態寫入到文件中。代碼如下:
boolean finish = false;
while (!finish) {
for (int i = 0; i < threads.length; i++) {
if (threads[i].getState() != status[i]) {
writeThreadInfo(pw, threads[i], status[i]);
status[i] = threads[i].getState();
}
}
finish = true;
for (int i = 0; i < threads.length; i++) {
finish = finish
&& (threads[i].getState() == Thread.State.TERMINATED);
}
}
11.實現writeThreadInfo方法,該方法將線程的ID、名稱、優先級、舊的狀態、新的狀態寫入到文件中。代碼如下:
/**
* 將一個線程的狀態輸出到一個文件中。
*
* @param pw PrintWriter對象
* @param thread 需要輸出狀態的線程對象
* @param state 線程的舊狀態
*/
private static void writeThreadInfo(PrintWriter pw,
Thread thread, Thread.State state) {
pw.printf("Main : Id %d = %s\n", thread.getId(), thread.getName());
pw.printf("Main : Priority: %d\n", thread.getPriority());
pw.printf("Main : Old State: %s\n", state);
pw.printf("Main : New State: %s\n", thread.getState());
pw.printf("Main : ********************************\n");
}
12.運行該示例,然后打開thread.log文件,查看所有線程的演化過程。
知其所以然
下面是thread.log文件的內容片段。從文件內容可以看出,高優先級的線程大致比低優先級的線程較早完成執行。另外,也可以看到每個線程的狀態演化過程。
Main : ********************************
Main : Id 11 = Thread-2
Main : Priority: 10
Main : Old State: BLOCKED
Main : New State: TERMINATED
Main : ********************************
Main : Id 13 = Thread-4
Main : Priority: 10
Main : Old State: BLOCKED
Main : New State: TERMINATED
Main : ********************************
Main : Id 14 = Thread-5
Main : Priority: 1
Main : Old State: BLOCKED
Main : New State: TERMINATED
Main : ********************************
下面是控制臺的輸出片段。輸出的是每個線程計算的乘法表,以及所有的線程計算過程。同時,從這里可以更細粒度地看到每個線程的演化過程。
Thread-8: 8 * 2 = 16
Thread-8: 8 * 3 = 24
Thread-8: 8 * 4 = 32
Thread-6: 6 * 0 = 0
Thread-6: 6 * 1 = 6
Thread-6: 6 * 2 = 12
Thread-6: 6 * 3 = 18
Thread-6: 6 * 4 = 24
Thread-6: 6 * 5 = 30
Thread-6: 6 * 6 = 36
Thread-6: 6 * 7 = 42
Thread-6: 6 * 8 = 48
Thread-6: 6 * 9 = 54
Thread-5: 5 * 0 = 0
Thread-5: 5 * 1 = 5
Thread-5: 5 * 2 = 10
Thread-5: 5 * 3 = 15
Thread-5: 5 * 4 = 20
Thread類有可以存儲線程信息所需的所有屬性。Java虛擬機使用線程優先級來每個時刻調度一個線程來使用CPU,并且根據線程的情況來設置其每個線程的狀態。
如果沒有設置線程的名稱,Java虛擬機會使用這種格式來時分配一個名稱,Thread-XX,其中XX是一個數字。我們不能修改線程的ID以及線程的狀態。Thread類也沒有實現setId()和setStatus()方法,以允許做出這些修改。
永無止境
在本節,我們學習了如何使用Thread對象來訪問線程信息。其實,Runnable的實現類也運行我們訪問這些信息。Thread類的靜態方法currentThread()可以獲取正在執行的Runnable實現類的對象,進而訪問線程的信息。
需要注意的是,如果嘗試設置1到10以外的優先級,setPriority()會拋出名為IllegalArgumentException的異常,
拿來主義
本文是從 《Java 7 Concurrency Cookbook》 (D瓜哥竊譯為 《Java7并發示例集》 )翻譯而來,僅作為學習資料使用。沒有授權,不得用于任何商業行為。
小有所成
Calculator類的完整代碼
package com.diguage.books.concurrencycookbook.chapter1.recipe2;
/**
* Date: 2013-09-13
* Time: 19:49
*/
public class Calculator implements Runnable {
private int number;
public Calculator(int number) {
this.number = number;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.printf("%s: %d * %d = %d\n",
Thread.currentThread().getName(),
number, i, i * number);
}
}
}
Main類的完整代碼
package com.diguage.books.concurrencycookbook.chapter1.recipe2;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
/**
* Date: 2013-09-13
* Time: 19:51
*/
public class Main {
public static void main(String[] args) {
Thread[] threads = new Thread[10];
Thread.State[] status = new Thread.State[threads.length];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new Calculator(i));
if ((i % 2) == 0) {
threads[i].setPriority(Thread.MAX_PRIORITY);
} else {
threads[i].setPriority(Thread.MIN_PRIORITY);
}
threads[i].setName("Thread-" + i);
}
try (FileWriter file = new FileWriter("D:\\thread.log");
PrintWriter pw = new PrintWriter(file)) {
for (int i = 0; i < threads.length; i++) {
Thread thread = threads[i];
pw.println("Main: Status of Thread " + i +
" : " + threads[i].getState());
status[i] = threads[i].getState();
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
boolean finish = false;
while (!finish) {
for (int i = 0; i < threads.length; i++) {
if (threads[i].getState() != status[i]) {
writeThreadInfo(pw, threads[i], status[i]);
status[i] = threads[i].getState();
}
}
finish = true;
for (int i = 0; i < threads.length; i++) {
finish = finish
&& (threads[i].getState() == Thread.State.TERMINATED);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 將一個線程的狀態輸出到一個文件中。
*
* @param pw PrintWriter對象
* @param thread 需要輸出狀態的線程對象
* @param state 線程的舊狀態
*/
private static void writeThreadInfo(PrintWriter pw,
Thread thread, Thread.State state) {
pw.printf("Main : Id %d = %s\n",
thread.getId(), thread.getName());
pw.printf("Main : Priority: %d\n", thread.getPriority());
pw.printf("Main : Old State: %s\n", state);
pw.printf("Main : New State: %s\n", thread.getState());
pw.printf("Main : ********************************\n");
}
}