Java多線程編程之線程狀態與線程管理

一、線程狀態

在線程的生命週期中,線程會有幾種狀態,如圖所示:
在這裏插入圖片描述

(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
線程執行完成

以上內容僅供參考學習,如有侵權請聯繫我刪除!
如果這篇文章對您有幫助,左下角的大拇指就是對博主最大的鼓勵。
您的鼓勵就是博主最大的動力!

發佈了59 篇原創文章 · 獲贊 7 · 訪問量 2929
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章