天天看點

線程排程 java_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類的三個靜态常量

線程排程 java_Java多線程--線程的排程

1 @SpringBootTest2 //定義MaxPriority,實作Runnable接口

3 class MaxPriority implementsRunnable{4 //實作接口中的run()方法

5 @Override6 public voidrun() {7 for (int i = 0; i <5; i++) {8 System.out.println(Thread.currentThread().getName() + "正在輸出!");9 }10 }11 }12 //定義MinPriority,實作Runnable接口

13 class MinPriority implementsRunnable{14 //實作接口中的run()方法

15 @Override16 public voidrun() {17 for (int i = 0; i <5; i++) {18 System.out.println(Thread.currentThread().getName() + "正在輸出!");19 }20 }21 }22 public classExample {23 public static voidmain(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 }

程式運作結果:

線程排程 java_Java多線程--線程的排程

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

線程休眠

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

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

1 @SpringBootTest2 //定義SleepThread,實作Runnable接口

3 class SleepThread implementsRunnable{4 //實作接口中的run()方法

5 @Override6 public voidrun() {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 classExample {25 public static void main(String[] args) throwsException{26 //建立一個線程

27 new Thread(newSleepThread()).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 }

運作結果:

線程排程 java_Java多線程--線程的排程

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 @SpringBootTest2 //定義YieldThread,繼承Thread類

3 classYieldThread extends Thread{4 //定義一個有參的構造方法

5 publicYieldThread(String name) {6 super(name);7 }8 public voidrun() {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 classExample {19 public static voidmain(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 }

運作結果:

線程排程 java_Java多線程--線程的排程

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

線程插隊

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

1 @SpringBootTest2 //定義EmergencyThread類,實作Runnable接口

3 classEmergencyThread implements Runnable{4 //實作接口中的run()方法

5 @Override6 public voidrun() {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 classExample {18 public static voidmain(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

運作結果:

線程排程 java_Java多線程--線程的排程

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