Java多線程--線程的調度


       java虛擬機會按照特定的機制為程序中的每個線程分配CPU的使用權,這種機制被稱為線程的調度。  

  在計算機中,線程調度有兩種模型,分別是分時調度模型和搶占式調度模型。

分時調度模型:

  指讓所有的線程輪流獲得CPU的使用權,並且平均分配每個線程占用CPU的時間片。

搶占式調度模型:

  讓可運行遲中優先級高的線程優先占用CPU,而對於優先級相同的線程,隨機選擇一個線程使其占用CPU,當它失去了CPU的使用權后,再隨機選擇其它線程獲取CPU的使用權。

 

  java虛擬機默認采用搶占式調度模型,但在某些特定的需求下需要改變這種模型,由線程自己來控制CPU的調度。

 

線程的優先級:

  如果要對線程進行調度,最直接的方法就是設置線程的優先級。 

  在線程中有優先級的機制,線程的優先級用1~10之間的整數來表示,數字越大則表示優先級越高。除了數字,還可以使用Thread類中提供的三個靜態常量表示線程的優先級,他們分別是:MAX_PRIORITY、MIN_PRIORITY、NORM_PRIORIY。

  優先級高的線程獲得CPU執行的機會越大,而優先級低的線程獲得CPU執行的機會越小。在默認情況下,每個線程都有自己的優先級,例如main線程具有普通優先級。線程優先級不是固定不變的,通過調用Thread類的setPriority(int newPriority)方法可以進行改變,setPriority()方法的數newPriority接收1~10之間的數或者Thread類的三個靜態常量

 

 1 @SpringBootTest
 2 //定義MaxPriority,實現Runnable接口
 3 class MaxPriority implements Runnable{
 4     //實現接口中的run()方法
 5     @Override
 6     public void run() {
 7         for (int i = 0; i <5; i++) {
 8             System.out.println(Thread.currentThread().getName() + "正在輸出!");
 9         }
10     }
11 }
12 //定義MinPriority,實現Runnable接口
13 class MinPriority implements Runnable{
14     //實現接口中的run()方法
15     @Override
16     public void run() {
17         for (int i = 0; i <5; i++) {
18             System.out.println(Thread.currentThread().getName() + "正在輸出!");
19         }
20     }
21 }
22 public class Example {
23     public static void main(String[] args) {
24         //創建兩個線程
25         Thread minPriority = new Thread(new MinPriority(),"優先級較低的線程");
26         Thread maxPriority = new Thread(new MaxPriority(),"優先級較高的線程");
27         //設置線程的優先級為1
28         minPriority.setPriority(1);
29         //設置線程的優先級為10
30         maxPriority.setPriority(10);
31         //開啟兩個線程
32         minPriority.start();
33         maxPriority.start();
34     }
35  }

程序運行結果:

   優先級較高的maxPriority線程會先運行,運行完畢后優先級較低的minPriority線程才開始運行。

 

線程休眠

  如果希望人為地控制線程,使正在運行的線程暫停,將CPU讓給別的線程,這時可以使用靜態方法sleep(long millis)方法,該方法可以讓當前正在運行的線程暫停一段時間,進入休眠等待狀態。當前線程調用sleep(long millis)方法后,在指定時間(參數 millis)內是不會執行的,這樣其它的線程就可以得到執行的機會了。

  sleep(long millis)方法聲明拋出InterruptedExcption異常,因此在調用該方法時應該捕獲異常,或者聲明拋出異常。

 1 @SpringBootTest
 2 //定義SleepThread,實現Runnable接口
 3 class SleepThread implements Runnable{
 4     //實現接口中的run()方法
 5     @Override
 6     public void run() {
 7         for (int i = 0; i <5; i++) {
 8             if (i == 3){
 9                 try {
10                     Thread.sleep(2000);//當前線程休眠2秒
11                 }catch (InterruptedException e){
12                     e.printStackTrace();
13                 }
14             }
15             System.out.println("線程一正在輸出:" + i);
16             try {
17                 Thread.sleep(500); //當前線程休眠500毫秒
18             }catch (Exception e){
19                 e.printStackTrace();
20             }
21         }
22     }
23 }
24 public class Example {
25     public static void main(String[] args) throws Exception{
26         //創建一個線程
27         new Thread(new SleepThread()).start();
28         for (int i = 0; i < 10; i++) {
29             if (i ==5){
30                 Thread.sleep(2000);//當前線程休眠2秒
31             }
32             System.out.println("主線程正在輸出:" + i);
33             Thread.sleep(500); //當前線程休眠500毫秒
34         }
35     }
36  }

運行結果:

  17行和33行的目的是是讓一個線程在一次打印后休眠500ms,從而使另一個線程獲得執行機會,這樣就可以實現兩個線程的交替實行。

  在線程一的for循環中,當i==3時,調用Thread的sleep(2000)方法,使線程休眠2s,主線程得到執行機會,接着輸出了3和4,這說明線程一進入了休眠等待狀態。

  在主線程的for循環中,當i ==5時,也調用了Thread的sleep(2000)方法,使線程休眠2s,當主線程2s休眠結束后,兩個線程才會恢復交替執行。

  sleep()是靜態方法,只能控制當前正在運行的線程休眠,而不能控制其他線程休眠。當線程休眠結束后,線程就會返回就緒狀態,而不是立刻開始運行。

 

線程讓步

  線程讓步可以通過yield()方法來實現,該方法和sleep()方法有點相似,都可以讓當前正在運行的線程暫停,區別在於yield()方法不會阻塞線程,它只是將線程轉換成就緒狀態,讓系統的調度器重新調度一次。yield()方法結束后,只有與當前線程優先級相同或者更高的線程才能獲得執行機會。

 1 @SpringBootTest
 2 //定義YieldThread,繼承Thread類
 3 class YieldThread extends Thread{
 4     //定義一個有參的構造方法
 5     public YieldThread(String name) {
 6         super(name);
 7     }
 8     public void run() {
 9         for (int i = 0; i <5; i++) {
10             System.out.println(Thread.currentThread().getName() + "---" + i);
11             if (i == 3) {
12                 System.out.println("線程讓步:");
13                 Thread.yield();
14             }
15         }
16     }
17 }
18 public class Example {
19     public static void main(String[] args) throws Exception{
20         //創建兩個線程
21        Thread t1 = new YieldThread("線程A");
22        Thread t2 = new YieldThread("線程B");
23          //開啟兩個線程
24         t1.start();
25         t2.start();
26     }
27  }

運行結果:

 

   如圖,當線程B輸出3以后,會做出讓步,線程A繼續執行,同樣,當線程A輸出3后,也會做出讓步,線程B繼續執行。

 

線程插隊

  當在某個線程中調用其他線程的join()方法時,調用的線程將被阻塞,直到被join()方法加入的線程執行完成之后它才會繼續運行。

 1 @SpringBootTest
 2 //定義EmergencyThread類,實現Runnable接口
 3 class EmergencyThread implements Runnable{
 4     //實現接口中的run()方法
 5     @Override
 6     public void run() {
 7         for (int i = 1; i <6; i++) {
 8             System.out.println(Thread.currentThread().getName() + "輸入:" + i);
 9                 try {
10                     Thread.sleep(500);//當前線程休眠500毫秒
11                 }catch (InterruptedException e){
12                     e.printStackTrace();
13                 }
14         }
15     }
16 }
17 public class Example {
18     public static void main(String[] args) throws Exception{
19         //創建線程
20          Thread t = new Thread (new EmergencyThread(),"線程一");
21          t.start();
22         for (int i = 1; i < 6; i++) {
23             System.out.println(Thread.currentThread().getName() +"輸入:"+ i);
24             if (i ==2){
25                 t.join(); //調用join()方法
26             }
27             Thread.sleep(500); //當前線程休眠500毫秒
28         }
29     }
30  }
31  

運行結果:

   main線程中開啟了一個線程t,兩個線程的循環體中都調用了Thread的sleep(500)方法,以實現兩個線程的交替執行。當main()線程中的循環變量為2時,調用t線程的join()方法,這時,t線程就會“插隊”優先執行。從運行結果來看,當main線程輸出2后,線程一就開始執行,直到線程一執行完畢,main線程才繼續執行。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM