四、線程排程
線程排程管理器負責線程排隊和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( )方法 |
---|---|
使目前線程進入被阻塞狀态 | 将目前線程轉入暫停執行狀态 |
即使沒有其他等待運作的線程,目前線程也會等待指定的時間 | 如果沒有其他等待的線程,目前線程會馬上恢複執行 |
其他等待執行的線程的機會是均等的 | 會将優先級相同或更高的線程運作 |