一、線程狀態
在線程的生命週期中,線程會有幾種狀態,如圖所示:
(1)新建狀態
新建狀態(New)是通過new等方式創建線程對象,它僅僅是一個空的線程對象。
(2)就緒狀態
當主線程調用新建線程的start()方法後,它就進入就緒狀態(Runnable)。此時的線程尚未真正開始執行run()方法,它必須等待CPU的調度。
(3)運行狀態
CPU的調度就緒狀態的線程,線程進入運行狀態(Running),處於運行狀態的線程獨佔CPU, 執行run()方法。
(4)阻塞狀態
因爲某種原因運行狀態的線程會進入不可運行狀態,即阻塞狀態(Blocked),處於阻塞狀態的 線程JVM系統不能執行該線程,即使CPU空閒,也不能執行該線程。如下幾個原因會導致線程 進入阻塞狀態:
- 當前線程調用sleep()方法,進入休眠狀態。
- 被其他線程調用了join()方法,等待其他線程結束。
- 發出I/O請求,等待I/O操作完成期間。
- 當前線程調用wait()方法。
處於阻塞狀態可以重新回到就緒狀態,如:休眠結束、其他線程加入、I/O操作完成和調用 notify或notifyAll喚醒wait線程。
(5)死亡狀態
線程退出run()方法後,就會進入死亡狀態(Dead),線程進入死亡狀態有可以是正常實現完成
run()方法進入,也可能是由於發生異常而進入的。
二、線程管理
(1)線程優先級
線程的調度程序根據線程決定每次線程應當何時運行,Java提供了10種優先級,分別用1~10整數表 示,最高優先級是10用常量MAX_PRIORITY表示;最低優先級是1用常量MIN_PRIORITY;默認優先 級是5用常量NORM_PRIORITY表示。
Thread類提供了setPriority(int newPriority)方法可以設置線程優先級,通過getPriority()方法獲得線程優 先級。
設置線程優先級示例代碼如下:
public class HelloWorld {
public static void main(String[] args) {
// 創建線程t1,參數是一個線程執行對象Runner
Thread t1 = new Thread(new Runner());
// 設置線程最高優先級
t1.setPriority(Thread.MAX_PRIORITY);
// 線程開始
t1.start();
// 創建線程t2,參數是一個線程執行對象Runner
Thread t2 = new Thread(new Runner());
// 設置線程最低優先級
t2.setPriority(Thread.MIN_PRIORITY);
// 線程開始
t2.start();
// 創建線程t3,參數是一個線程執行對象Runner
Thread t3 = new Thread(new Runner());
// 設置線程6優先級
t3.setPriority(6);
// 線程開始
t3.start();
}
}
多次運行上面的示例會發現,t1線程經常先運行,但是偶爾t2線程也會先運行。這些現象
說明了:影響線程獲得CPU時間的因素,除了受到的線程優先級外,還與操作系統有關。
(2)等待線程結束
在介紹現在狀態時提到過join()方法,當前線程調用t1線程的join()方法,則阻塞當前線程,等待t1線程 結束,如果t1線程結束或等待超時,則當前線程回到就緒狀態。
Thread類提供了多個版本的join(),它們定義如下:
- void join():等待該線程結束。
- void join(long millis):等待該線程結束的時間最長爲millis毫秒。如果超時爲0意味着要一直等下 去。
- void join(long millis, int nanos):等待該線程結束的時間最長爲millis毫秒加nanos納秒。
使用join()方法示例代碼如下:
public class HelloWorld {
// 共享變量
static int value = 0;
public static void main(String[] args) throws InterruptedException {
System.out.println("主線程開始。。。");
// 創建線程t1
Thread t1 = new Thread(() ->{
System.out.println("線程ThreadA開始。。。");
for (int i = 0;i<2;i++){
System.out.println("線程ThreadA執行。。。");
value++;
}
System.out.println("線程ThreadA結束。。。");
},"線程ThreadA");
// 線程開始
t1.start();
// 主線程被阻塞,等待線程t1結束
t1.join();
System.out.println("value="+value);
System.out.println("主線程結束。。。。。");
}
}
運行結果:
主線程開始。。。
線程ThreadA開始。。。
線程ThreadA執行。。。
線程ThreadA執行。。。
線程ThreadA結束。。。
value=2
主線程結束。。。。。
如果將t1.join();這一行代碼註釋掉,則運行結果如下:
主線程開始。。。
value=0
線程ThreadA開始。。。
線程ThreadA執行。。。
線程ThreadA執行。。。
線程ThreadA結束。。。
主線程結束。。。。。
使用join()方法的場景是,一個線程依賴於另外一個線程的運行結果,所以調用另一個線程 的join()方法等它運行完成。
(3)線程讓步
線程類Thread還提供一個靜態方法yield(),調用yield()方法能夠使當前線程給其他線程讓步。它類似於 sleep()方法,能夠使運行狀態的線程放棄CPU使用權,暫停片刻,然後重新回到就緒狀態。與sleep()方 法不同的是,sleep()方法是線程進行休眠,能夠給其他線程運行的機會,無論線程優先級高低都有機 會運行。而yield()方法只給相同優先級或更高優先級線程機會。
代碼如下:
//編寫線程執行類
public class Runner1 implements Runnable {
//編寫線程執行代碼
@Override
public void run() {
for (int i = 0;i<10;i++){
// 打印線程次數和線程的名字
System.out.printf("第%d次執行--%s\n",i,Thread.currentThread().getName());
// 線程讓步
Thread.yield();
}
// 線程執行結束
System.out.println("執行完成"+Thread.currentThread().getName());
}
}
測試代碼:
public class HelloWorld {
public static void main(String[] args) {
// 創建線程t1,參數是一個線程執行對象Runner
Thread t1 = new Thread(new Runner1());
// 設置線程最高優先級
t1.setPriority(Thread.MAX_PRIORITY);
// 線程開始
t1.start();
}
}
相比較,yield()方法只能給相同優先級或更高優先級的線程讓步,sleep()方法能夠給其他線程運行的機會,無論線程優先級高低都有機 會運行。
sleep()方法可以控制時間,而yield()方法不能。
(4)線程停止
線程體中的run()方法結束,線程進入死亡狀態,線程就停止了。但是有些業務比較複雜,例如想開發 一個下載程序,每隔一段執行一次下載任務,下載任務一般會在由子線程執行的,休眠一段時間再執 行。這個下載子線程中會有一個死循環,但是爲了能夠停止子線程,設置一個結束變量。
代碼如下:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class HelloWorld {
private static String command = "";
public static void main(String[] args) {
// 創建線程t1,參數是一個線程執行對象Runner
Thread t1 = new Thread(() -> {
// 一直循環
while (!command.equalsIgnoreCase("exit")){
// 線程開始工作
// TODO
System.out.println("下載中");
try {
// 線程休眠
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 線程執行結束
System.out.println("線程執行完成");
});
// 開始線程t1
t1.start();
try( InputStreamReader ir = new InputStreamReader(System.in);
BufferedReader in = new BufferedReader(ir)){
// 從鍵盤接受一個字符串的輸入
command = in.readLine();
} catch (IOException e) {
e.printStackTrace();
}
}
}
用戶在控制檯輸入的是否爲exit 字符串,如果不是則進行循環,否則結束循環,結束循環就結束了run()方法,線程就停止了。
運行結果如下:
下載中
下載中
下載中
下載中
下載中
exit
線程執行完成
以上內容僅供參考學習,如有侵權請聯繫我刪除!
如果這篇文章對您有幫助,左下角的大拇指就是對博主最大的鼓勵。
您的鼓勵就是博主最大的動力!