天天看點

線程與多線程(四)——線程排程

四、線程排程

  線程排程管理器負責線程排隊和CPU線上程間的配置設定,并按線程排程算法進行排程。當線程排程管理器選中某個線程時,該線程獲得 CPU資源進人運作狀态。

  線程排程是搶占式排程,即如果在目前線程執行過程中個更高優先級的線程進人可運作狀态,則這個更高優先級的線程立即被排程執行。

3.1實作線程排程的方法

1. join( )方法

  join( )方法使目前線程暫停執行,等待調用該方法的線程結束後再繼續執行本線程。它被重載了3次:

public final void join() 
public final void join(long mills)
public final void join(long mills,int nanos)
           

下面通過示例具體看一下join( )方法的應用。

示例1

  使用join( )方法阻塞線程。

  實作步驟:

  (1)定義線程類,輸出5次目前線程的名稱。

  (2)定義測試類,使用join()方法阻塞主線程。

// 線程類
public class MyThread extends Thread(
    public MyThread(string name){
        super(name);
    }
    public void run(){
    for(int i=;i<;i++){
        //輸出目前線程的名稱
        System.out.println(Thread.currentThread().getName() + "-" + i);
    } 
}
// 測試類
public class Test{
    /*
    * Java 程式中的public static void main( )方法是主線程的入口,
    * 運作Java程式時,會先執行這個方法。
    */
    public static void main(String[] args) {
        for (int i = ; i < ; i++) {
            if (i == ) {
                System.out.println("i:" + i);
                // 主線程運作5次後,開始myThread的線程
                MyThread myThread = new MyThread("myThread");
                try {
                    myThread.start();
                    // join()方法使目前線程暫停執行,等待調用該方法的線程結束後再繼續執行本線程
                    // 把該線程通過join()方法插入到主線程前面,否則主線程執行完畢後再執行該線程
                    myThread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // main,主線程
            System.out.println(Thread.currentThread().getName() + "-" + i);
        }
    }
}
// 再解釋一遍流程。
// 1、主線程開始運作。
// 2、i等于5的時候,主線程暫停。
// 3、等待調用該方法的線程myThread執行完畢後繼續執行主線程main。
main-
main-
main-
main-
main-
i:
myThread-
myThread-
myThread-
myThread-
myThread-
main-
main-
main-
main-
main-
           

  在示例1中,使用join( )方法阻塞指定的線程等到另一個線程完成以後再繼續執行。其中myThread.join( )表示讓目前線程即主線程main加到myThread的末尾,主線程被阻塞,myThread執行完以後主線程才能繼續執行。

  Thread.curentThread().geName( )擷取了目前線程的名稱。

  從線程傳回資料時也經常使用到join()方法。

示例2

  使用join( )方法實作兩個線程間的資料傳遞。

  實作步驟:

  (1)定義線程類,為變量指派。

  (2)定義測試類。

public class MyThread extends Thread{
    public String valuel;
    public String value2;   
    public void run() {
        valuel = "value1已指派";
        value2 = "value2已指派";
    }
}
public class Test {
    public static void main(Stringl[] args) throws InterruptedException{
        MyThread thread = new MyThread();
        thread.start();
        System.out.println("value1 :"+thread.value1);
        System.out.printin("value2 :"+thread.value2);
    }
}
// 輸出結果:
valuel: null
value2: null
           

  在示例2中,在run( )方法中已經對value1和value2指派,而傳回的卻是null,發生這種情況的原因是在主線程中調用start( )方法後就立刻輸出了value1和value2的值,而這裡run( )方法可能還沒有執行到為valuel 和value2指派的語句。要避免這種情況的發生,需要等run( )方法執行完後才執行輸出value1和value2的代碼,可以使用join( )方法來解決這個問題。修改示例2的代碼,在“thread.start( );”後添加”thread.join();”,修改後此處代碼如下:

public class Test {
    public static void main(Stringl[] args) throws InterruptedException{
        MyThread thread = new MyThread();
        thread.start();
        // join()方法
        thread.join();
        System.out.println("value1 :"+thread.value1);
        System.out.printin("value2 :"+thread.value2);
    }
}
// 重新運作示例2,則可以得到如下輸出結果:
valuel: value1 己指派
value2: value2 已指派
           

—————————— 我是手動分割線 ——————————

2.sleep( )方法

  sleep( )方法定義文法如下:

  public static void sleep(long millis)

  sleep( )方法會讓當 前線程睡眠(停止執行) millis毫秒,線程由運作中的狀态進人不可運作狀态,

睡眠時間過後線程會再進人可運作狀态。

示例1

  使用sleep( )方法阻塞線程。

  實作步驟:

  (1)定義線程。

  (2)在run( )方法中使用sleep( )方法阻塞線程。

  (3)定義測試類。

public class SleepThreadTest {
    public static void bySec(long s) {
        for (int i = ; i < s; i++) {
            try {
                // 睡眠1000毫秒
                Thread.sleep();
                System.out.println("線程---"+Thread.currentThread().getName()+" 等待---"+ (i + ) + "秒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        System.out.println("please wait...");
        // 讓主(該)線程等等5秒再執行
        SleepThreadTest.bySec();
        System.out.println("start...");
    }
}
// 運作結果
please wait...
線程---main 等待---秒
線程---main 等待---秒
線程---main 等待---秒
線程---main 等待---秒
線程---main 等待---秒
start...
           

  示例3的代碼中,在執行主線程以後,首先輸出了please wait,然後主線程等待5秒後繼續執行。

—————————— 我是手動分割線 ——————————

3.yield( )方法

  yield( )方法定義文法如下:

  public static void yield()

  yield( )方法可暫停目前線程執行,允許其他線程執行, 該線程仍處于可運作狀态,不轉為阻塞狀态。此時,系統選擇其他相同或更高優先級線程執行,若無其他相同或更高優先級線程,則該線程繼續執行。

示例1

  使用yield()方法暫停線程。

  實作步驟:

  (1)定義兩個線程。

  (2)在run( )方法中使用yield( )方法暫停線程。

  (3)定義測試類。

// 線程類1
public class FirstThread extends Thread {
    @Override
    public void run() {
        for (int i = ; i < ; i++) {
            System.out.println("第一個線程的第" + (i + ) + "次運作" + Thread.currentThread().getName());
            // 暫停線程
            Thread.yield();
        }
    }
}
// 線程類2
public class SecThread extends Thread {
    @Override
    public void run() {
        for (int i = ; i < ; i++) {
            System.out.println("第二個線程的第" + (i + ) + "次運作" + Thread.currentThread().getName());
            // 暫停線程
            Thread.yield();
        }
    }
}
// 測試類
public class Test {
    public static void main(String[] args) {
        FirstThread t1 = new FirstThread();
        SecThread t2 = new SecThread();
        t1.start();
        t2.start();
    }
}

// 運作結果,兩個線程類都用了yield()方法,它們都還是可運作狀态,如果沒有相同更高優先級線程,它們互相交替執行
第一個線程的第次運作Thread-
第一個線程的第次運作Thread-
第一個線程的第次運作Thread-
第一個線程的第次運作Thread-
第二個線程的第次運作Thread-
第二個線程的第次運作Thread-
第二個線程的第次運作Thread-
第二個線程的第次運作Thread-
第二個線程的第次運作Thread-
第一個線程的第次運作Thread- // 又是第一個線程
           

  在示例6中,調用了yield( )方法之後,目前線程并不是轉人被阻塞狀态,它可以與其他等待執行的線程競争CPU資源,如果此時它又搶占到CPU資源,就會出現連續運作幾次的情況。sleep( )方法與yield( )方法在使用時容易混淆,這兩個方法之間的差別如下表所示。

sleep( )方法 yield( )方法
使目前線程進入被阻塞狀态 将目前線程轉入暫停執行狀态
即使沒有其他等待運作的線程,目前線程也會等待指定的時間 如果沒有其他等待的線程,目前線程會馬上恢複執行
其他等待執行的線程的機會是均等的 會将優先級相同或更高的線程運作

繼續閱讀