Java線程的調度與優先級詳解

由於CPU的計算頻率非常高,每秒計算數十億次,因此可以將CPU的時間從毫秒的維度進行分段,每一小段叫作一個CPU時間片。

目前操作系統中主流的線程調度方式是:基於CPU時間片方式進行線程調度。線程隻有得到CPU時間片才能執行指令,處於執行狀態,沒有得到時間片的線程處於就緒狀態,等待系統分配下一個CPU時間片。由於時間片非常短,在各個線程之間快速地切換,因此表現出來的特征是很多個線程在“同時執行”或者“並發執行”。

線程的調度模型目前主要分為兩種:分時調度模型和搶占式調度模型。

(1) 分時調度模型:系統平均分配CPU的時間片,所有線程輪流占用CPU,即在時間片調度的分配上所有線程“人人平等”

(2) 搶占式調度模型:系統按照線程優先級分配CPU時間片。優先級高的線程優先分配CPU時間片,如果所有就緒線程的優先級相同,那麼會隨機選擇一個,優先級高的線程獲取的CPU時間片相對多一些。

由於目前大部分操作系統都是使用搶占式調度模型進行線程調度,Java的線程管理和調度是委托給操作系統完成的,與之相對應,Java的線程調度也是使用搶占式調度模型,因此Java的線程都有優先級。

在Thread類中有一個實例屬性和兩個實例方法,專門用於進行線程優先級相關的操作。與線程優先級相關的成員屬性為:

// 保存Thread線程實例的優先級,1~10之間
private int priority;
// 獲取線程優先級
public final int getPriority(){//...}
// 設置線程優先級
public final void setPriority(int priority){//...}

Thread實例的priority屬性默認是級別5,對應的類常量是NORM_PRIORITY。優先級最大值為10,最小值為1,Thread類中定義的三個優先級常量如下:

 public final static int MIN_PRIORITY = 1;
 public final static int NORM_PRIORITY = 5;
 public final static int MAX_PRIORITY = 10;

Java中使用搶占式調度模型進行線程調度。priority實例屬性的優先級越高,線程獲得CPU時間片的機會就越多,但也不是絕對的。

示例:

1、定義一個線程執行體,異步執行:

public class ThreadDemo extends Thread {
    private  long num = 0;
    public long getNum() {
        return num;
    }
    @Override
    public void run() {
        // 線程執行體:死循環
       for(int i=0;;i++){
           num++;
       }
    }
}

2、創建10個線程,並設置不同的線程優先級,來執行線程執行體:

public class Main {
    public static void main(String[] args) throws InterruptedException {
        ThreadDemo[] threads = new ThreadDemo[10];
        for(int i=0;i<threads.length;i++){
            threads[i] = new ThreadDemo();
            // 設置線程優先級1~10
            threads[i].setPriority(i+1);
        }
        // 啟動線程
        for(int i=0;i<threads.length;i++){
            threads[i].start();
        }
        // 等待線程1s
        Thread.sleep(1000);
        // 停止線程
        for(int i=0;i<threads.length;i++){
            threads[i].stop();
        }
        for(int i=0;i<threads.length;i++){
            System.out.println(threads[i].getName()
                    +"-優先級為-"+threads[i].getPriority()
                    +"-機會值為-"+threads[i].getNum());
        }
    }
}

在線程的run()方法中,設置瞭一個沒有條件判斷表達式的for循環,這是一個死循環,線程啟動之後,永遠也不會退出,直到線程被停止。那麼,問題來瞭:如何停止這10個線程呢?這裡使用Thread類的stop()實例方法,該方法的作用是終止線程的執行。

Thread類的stop()實例方法是一個過時的方法,也是一個不安全的方法。這裡的安全指的是系統資源(文件、網絡連接等)的安全——stop()實例方法可能導致資源狀態不一致,或者說資源出現問題時很難定位。在實際開發過程中,不建議使用stop()實例方法。

3、運行結果:

Thread-0-優先級為-1-機會值為-0
Thread-1-優先級為-2-機會值為-0
Thread-2-優先級為-3-機會值為-0
Thread-3-優先級為-4-機會值為-0
Thread-4-優先級為-5-機會值為-3038296
Thread-5-優先級為-6-機會值為-4473657
Thread-6-優先級為-7-機會值為-2521154868
Thread-7-優先級為-8-機會值為-2537430692
Thread-8-優先級為-9-機會值為-2708120258
Thread-9-優先級為-10-機會值為-2690953898

演示示例中10個線程停下來之後,某個線程的實例屬性opportunities的值越大,就表明該線程獲得的CPU時間片越多。分析案例的執行結果,可以得出以下結論:

(1) 整體而言,高優先級的線程獲得的執行機會更多。從實例中可以看到:優先級在5級以上的線程執行機會明顯偏多,整體對比非常明顯。

(2) 執行機會的獲取具有隨機性,優先級高的不一定獲得的機會多。比如,例子中的thread-9比thread-8優先級高,但是thread-9所獲得的機會反而偏少。

註意:

(1) 線程優先級會提示調度器優先調度該線程,它僅僅是一個提示,調度器可以忽略它。

(2) 如果CPU比較忙,那麼優先級高的線程會獲得更多的時間片,但是CPU閑時,優先級幾乎沒作用。

總結

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容! 

推薦閱讀: