Java多線程學習(四)---控制線程

控制線程

摘要java

Java的線程支持提供了一些便捷的工具方法,經過這些便捷的工具方法能夠很好地控制線程的執行併發

1. join線程控制,讓一個線程等待另外一個線程完成的方法工具

2. 後臺線程,又稱爲守護線程或精靈線程。它的任務是爲其餘的線程提供服務,若是全部的前臺線程都死亡,後臺線程會自動死亡spa

3. 線程睡眠sleep,讓當前正在執行的線程暫停一段時,並進入阻塞狀態線程

4. 線程讓步yield,讓當前正在執行的線程暫停,但它不會阻塞該線程,它只是將該線程轉入就緒狀態code

1、join線程

Thread提供了讓一個線程等待另外一個線程完成的方法join()方法。當在某個程序執行流中調用其餘線程的join()方法時,調用線程將被阻塞,直到被join()方法加入的join線程執行完爲止。join()方法一般由使用線程的程序調用,以將大問題劃分紅許多小問題,每一個小問題分配一個線程。當全部的小問題都獲得處理後,再調用主線程來進一步操做。對象

  1. package test;
  2.  
  3. public class JoinThread extends Thread {
  4.    // 提供一個有參數的構造器,用於設置該線程的名字
  5.    public JoinThread(String name) {
  6.       super(name);
  7.    }
  8.  
  9.    // 重寫 run方法,定義線程執行體
  10.    public void run() {
  11.       for (int i = 0; i < 100; i++) {
  12.          System.out.println(getName() + "" + i);
  13.       }
  14.    }
  15.  
  16.    public static void main(String[] args) throws Exception {
  17.       // 啓動子線程
  18.       new JoinThread(" 新線程 ").start();
  19.       for (int i = 0; i < 100; i++) {
  20.          if (i == 20) {
  21.             JoinThread jt = new JoinThread(" Join的線程 ");
  22.             jt.start();
  23.             // main 線程調用了 jt線程的join()方法,main線程
  24.             // 必須等 jt執行結束纔會向下執行
  25.             jt.join();
  26.          }
  27.          System.out.println(Thread.currentThread().getName() + "" + i);
  28.       }
  29.    }
  30. }

運行結果: blog

main 0 資源

main 1 字符串

main 2

main 3

新線程 0

main 4

新線程 1

main 5

新線程 2

main 6

新線程 3

main 7

新線程 4

main 8

新線程 5

main 9

新線程 6

新線程 7

新線程 8

main 10

新線程 9

main 11

新線程 10

main 12

新線程 11

main 13

新線程 12

main 14

新線程 13

main 15

新線程 14

main 16

…………

main 18

新線程 17

main 19

新線程 18

新線程 19

…………

新線程 99

被Join的線程 0

…………

被Join的線程 99

main 20

上面程序中一共有3個線程,主方法開始時就啓動了名爲"新線程"的子線程,該子線程將會和main線程併發執行。當主線程的循環變量i等於20時啓動了名爲"被Join的線程"的線程,該線程不會和main線程併發執行。main線程必須等該線程執行結束後才能夠向下執行。在名爲"被Join的線程"的線程執行時,實際上只有2個子線程併發執行而主線程處於等待狀態。運行上面程序。從上面的運行結果能夠看出,主線程執行到i=20時啓動,並join了名爲"被Join的線程"的線程,因此主線程將一直處於阻塞狀態,直到名爲"被Join的線程"的線程執行完成。

2、後臺線程

有一種線程,它是在後臺運行的,它的任務是爲其餘的線程提供服務,這種線程被稱爲後臺線程Daemon Thread),又稱爲守護線程精靈線程JVM的垃圾回收線程就是典型的後臺線程。後臺線程有個特徵:若是全部的前臺線程都死亡,後臺線程會自動死亡。

調用Thread對象的setDaemon(true)方法可將指定線程設置成後臺線程。下面程序將執行線程設置成後臺線程,能夠看到當全部的前臺線程死亡時,後臺線程隨之死亡。當整個虛擬機中只剩下後臺線程時,程序就沒有繼續運行的必要了,因此虛擬機也就退出了。

  1. public class DaemonThread extends Thread {
  2.    // 定義後臺線程的線程執行體與普通線程沒有任何區別
  3.    public void run() {
  4.       for (int i = 0; i < 1000; i++) {
  5.          System.out.println(getName() + "" + i);
  6.       }
  7.    }
  8.  
  9.    public static void main(String[] args) {
  10.       DaemonThread t = new DaemonThread();
  11.       // 將此線程設置成後臺線程
  12.       t.setDaemon(true);
  13.       // 啓動後臺線程
  14.       t.start();
  15.       for (int i = 0; i < 10; i++) {
  16.          System.out.println(Thread.currentThread().getName() + "" + i);
  17.       }
  18.       // ----- 程序執行到此處,前臺線程( main線程)結束------
  19.       // 後臺線程也應該隨之結束
  20.    }
  21. }

運行結果:

main 0

Thread-0 0

main 1

Thread-0 1

main 2

Thread-0 2

main 3

Thread-0 3

main 4

Thread-0 4

main 5

Thread-0 5

main 6

Thread-0 6

main 7

Thread-0 7

main 8

Thread-0 8

main 9

Thread-0 9

Thread-0 10

Thread-0 11

Thread-0 12

Thread-0 13

Thread-0 14

Thread-0 15

Thread-0 16

Thread-0 17

Thread-0 18

Thread-0 19

上面程序中的t線程設置成後臺線程,而後啓動該線程,原本該線程應該執行到i等於999時纔會結束,但運行程序時不難發現該後臺線程沒法運行到999,由於當主線程也就是程序中惟一的前臺線程運行結束後,JVM會主動退出,於是後臺線程也就被結束了。Thread類還提供了一個isDaemon0方法,用於判斷指定線程是否爲後臺線程

從上面程序能夠看出,主線程默認是前臺線程, t線程默認也是前臺線程。並非全部的線程默認都是前臺線程,有些線程默認就是後臺線程——前臺線程建立的子線程默認是前臺線程後臺線程建立的子線程默認是後臺線程

前臺線程死亡後,JVM會通知後臺線程死亡,但從它接收指令到作出響應,須要必定時間。並且要將某個線程設置爲後臺線程,必須在該線程啓動以前設置,也就是說setDaemon(true)必須在start()方法以前調用,不然會引起IllegaIThreadStateException異常。

3、線程睡眠---sleep

若是須要讓當前正在執行的線程暫停一段時,並進入阻塞狀態,則能夠經過調用Thread類的靜態sleep()方法來實現。噹噹前線程調用sleep()方法進入阻塞狀態後,在其睡眠時間段內,該線程不會得到執行的機會,即便系統中沒有其餘可執行的線程,處於sleep()中的線程也不會執行,所以sleep()方法經常使用來暫停程序的執行。

下面程序調用sleep()方法來暫停主線程的執行,由於該程序只有一個主線程,當主線程進入睡眠後,系統沒有可執行的線程,因此能夠看到程序在sleep()方法處暫停

public class SleepTest {

    public static void main(String[] args) throws Exception {

        for (int i = 0; i < 10; i++) {

            System.out.println("當前時間: " + new Date());

            // 調用sleep方法讓當前線程暫停1s

            Thread.sleep(1000);

        }

    }

}

上面程序中sleep()方法將當前執行的線程暫停1秒,運行上面程序,看到程序依次輸出10條字符串,輸出2條字符串之間的時間間隔爲1秒。

4、線程讓步---yield()

yield()方法是一個和sleep()方法有點類似的方法,它也是Threard類提供的一個靜態方法,它也可讓當前正在執行的線程暫停,但它不會阻塞該線程,它只是將該線程轉入就緒狀態。yield()只是讓當前線程暫停一下,讓系統的線程調度器從新調度一次,徹底可能的狀況是:當某個線程調用了yield()方法暫停以後,線程調度器又將其調度出來從新執行。

實際上,當某個線程調用了yield()方法暫停以後,只有優先級與當前線程相同,或者優先級比當前線程更高的處於就緒狀態的線程纔會得到執行的機會。下面程序使用yield()方法來讓當前正在執行的線程暫停。

  1. public class YieldTest extends Thread {
  2.    public YieldTest(String name) {
  3.       super(name);
  4.    }
  5.  
  6.    // 定義 run方法做爲線程執行體
  7.    public void run() {
  8.       for (int i = 0; i < 50; i++) {
  9.          System.out.println(getName() + "" + i);
  10.          // i等於20時,使用yield方法讓當前線程讓步
  11.          if (i == 20) {
  12.             Thread.yield();
  13.          }
  14.       }
  15.    }
  16.  
  17.    public static void main(String[] args) throws Exception {
  18.       // 啓動兩條併發線程
  19.       YieldTest yt1 = new YieldTest(" 高級 ");
  20.       // ty1線程設置成最高優先級
  21.       // yt1.setPriority(Thread.MAX_PRIORITY);
  22.       yt1.start();
  23.       YieldTest yt2 = new YieldTest(" 低級 ");
  24.       // yt2線程設置成最低優先級
  25.       // yt2.setPriority(Thread.MIN_PRIORITY);
  26.       yt2.start();
  27.    }
  28. }

運行結果:

高級 0

低級 0

高級 1

低級 1

高級 2

低級 2

高級 3

低級 3

高級 4

低級 4

高級 5

低級 5

高級 6

低級 6

高級 7

低級 7

高級 8

低級 8

高級 9

低級 9

高級 10

低級 10

高級 11

低級 11

高級 12

低級 12

高級 13

低級 13

高級 14

低級 14

高級 15

低級 15

高級 16

低級 16

高級 17

低級 17

高級 18

低級 18

高級 19

低級 19

高級 20

低級 20

高級 21

低級 21

高級 22

低級 22

高級 23

低級 23

高級 24

低級 24

高級 25

……

低級 48

高級 49

低級 49

上面程序中調用的yield()靜態方法讓當前正在執行的線程暫停,讓系統線程調度器從新調度。因爲程序中第21行、第25行代碼處於註釋狀態——即兩個線程的優先級徹底同樣,因此當一個線程使用yield()方法暫停後,另外一個線程就會開始執行。若是將YieldTest.java程序中兩行代碼的註釋取消,也就是爲兩個線程分別設置不一樣的優先級,則程序的運行結果示。

高級 0

高級 1

高級 2

高級 3

高級 4

高級 5

高級 6

高級 7

高級 8

高級 9

高級 10

高級 11

高級 12

高級 13

高級 14

高級 15

高級 16

高級 17

高級 18

高級 19

高級 20

高級 21

高級 22

高級 23

高級 24

高級 25

高級 26

高級 27

高級 28

高級 29

高級 30

高級 31

高級 32

高級 33

高級 34

高級 35

高級 36

高級 37

高級 38

高級 39

高級 40

高級 41

高級 42

高級 43

高級 44

高級 45

高級 46

高級 47

高級 48

高級 49

低級 0

低級 1

………

低級 48

低級 49

關於sleep()方法和yield()方法的區別以下:

1. sleep()方法暫停當前線程後,會給其餘線程執行機會,不會理會其餘線程的優先級;但yield()方法只會給優先級相同,或優先級更高的線程執行機會

2. sleep()方法會將線程轉入阻塞狀態,直到通過阻塞時間纔會轉入就緒狀態:而yield()不會將線程轉入阻塞狀態,它只是強制當前線程進入就緒狀態。所以徹底有可能某個線程調用yield()方法暫停以後,當即再次得到處理器資源被執行

3. sleep()方法聲明拋出了InterruptcdException異常,因此調用sleep()方法時要麼捕捉該異常,要麼顯式聲明拋出該異常;而yield()方法則沒有聲明拋出任何異常

4. sleep()方法比yield()方法有更好的可移植性,一般不建議使用yield()方法來控制併發線程的執行

若是,您認爲閱讀這篇博客讓您有些收穫,不妨點擊一下右下角的【推薦】。
若是,您但願更容易地發現個人新博客,不妨點擊一下左下角的【關注我】。
若是,您對個人博客所講述的內容有興趣,請繼續關注個人後續博客,我是【Sunddenly】。

本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利

相關文章
相關標籤/搜索