Java多線程學習(三)Thread基本函數詳解(sleep、yield、join、interrupt、守護線程、優先級)

一、圖片總結:

1.1 補充知識

下圖是線程的的六種狀態

  1. 初始態
  2. 運行態(就緒是指等待CPU分配執行)
  3. 阻塞態
  4. 等待態
  5. 超時等待態
  6. 終止態

下圖取之Java線程狀態
在這裏插入圖片描述

1.2 函數整體總結

在這裏插入圖片描述

二、sleep、yield、join

2.1 sleep:

  • Api:
  1. sleep(long millis) //參數爲毫秒
  2. sleep(long millis,int nanoseconds) //第一參數爲毫秒,第二個參數爲納秒
  • 作用: sleep就是讓線程睡眠,交出CPU

注意點:但是值得注意的是它不會釋放鎖(即如果該線程持有該對象的鎖,那麼sleep後其他線程也無法訪問該對象。)

2.2 yield

  • Api:
  1. yield()
  • 作用: yield 讓當前線程交出CPU,但是不能控制時間

注意點: 不會釋放鎖,但是值得注意的是yield只能讓等於或大於自己優先級的線程有機會獲得CPU執行機會

2.3 join

  • 結論:先說結論
  1. join 是通過wait來實現的。
  2. 但是join不會釋放鎖,因爲他wait的線程是你執行thread.join所在的線程(一般是main線程)
  • Api:
  1. join()
  2. join(long millis) //參數爲毫秒
  3. join(long millis,int nanoseconds) //第一參數爲毫秒,第二個參數爲納秒
  • 作用:讓當前線程等待子線程結束之後才能運行

注意點:join實現的順序如下
1.假設在main線程裏運行了子線程A
2.接着設置 A.join()
3.接着main線程會處於waiting狀態
4.直到子線程完成後通知main線程結束等待
-------------特別注意的一個概念----------------->
1.主線程調用的方法就是主線程獲得鎖,子線程run方法裏的調用纔是子線程獲得鎖。
2.所以join通過wait來等待的是主線程,子線程是不會釋放鎖的

  • 例子
lic class JoinTest {
    public static int a = 0;
    public static void main(String ...args) throws InterruptedException {


        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<10;i++)
                    a++;
            }
        });
        
        thread.start();

        //在main線程裏執行了join,所以main線程獲得了join的鎖
        //後面join源碼分析裏執行wait是main線程(不要搞亂了)
        thread.join();
        println(a);


    }
//如果不加 join 正常情況下應該是小於10的
//但是加了 join 後,它會等待thread線程執行結束後再執行主線程
//所以答案等於10
  • 源碼解析
 public final void join(long millis) throws InterruptedException {
        synchronized(lock) {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
        //判斷子線程是否執行完畢,如果執行完畢的話進行等待
        //這邊等待對象是你執行 thread.join 所在的線程 即main
        //這邊只要子線程沒執行完畢就無限循環
            while (isAlive()) {
                lock.wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                lock.wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
        }
    }

三、interrupt

  • Api
  1. thread.interrupt() 通知線程中斷(只是一個信號,真正如何處理還是需要自己判斷)
  2. Thread.interrupted() 得到線程是否中斷的狀態並且重置狀態(false)
  3. Thread.currentThread().isInterrupted()得到線程是否中斷的狀態
  • 注意

調用interrupt()方法,立刻改變的是中斷狀態,但如果不是在阻塞態,就不會拋出異常;如果在進入阻塞態後,中斷狀態爲已中斷,就會立刻拋出異常(阻塞狀態 run方法無法執行得通過其他方法來判斷)

  • 例子
    這邊舉一個正在阻塞狀態中的例子,如果正常運行的話Run方法會正常執行只用通過判斷isInterrupted就可以來

業務重點
其中sleep被阻塞100秒這時候如果提前終止使用isInterrupted是不行的。我們通過阻塞時執行interrupt()會拋異常的特性來完成該操作,捕捉到異常後,會提前結束阻塞狀態,所以更改stop的屬性來挑出循環,等待run方法執行完畢。

 static boolean stop = true;

    public static void main(String... args) {


        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
            
                while (stop) {
                    try {
                        Thread.sleep(100000);
                    } catch (InterruptedException e) {
                        stop = false;
                    }

                }
            }
        });

        thread.start();
        thread.interrupt();
        
    }

四、優先級

  • Api
  1. setPriority
  • 作用:
    設置優先級,高優先級被優先執行的機率更大

  • 優先級等級(1-10):

  1. 最低優先級 1:Thread.MIN_PRIORITY
  2. 最高優先級 10:Thread.MAX_PRIORITY
  3. 普通優先級 5:Thread.NORM_PRIORITY

如果無設置優先級,默認時父線程的優先級,比如我們平時在主線程創建線程優先級都是5.

五、守護線程

  • 補充知識:
    守護線程是爲其他線程服務的,若只剩下守護線程,那麼守護線程就會自動中斷。
  • Api
  1. setDaemon(true)
  • 作用:設置爲守護線程
  • 注意點
  1. thread.setDaemon(true)必須在thread.start()之前設置
  2. 你不能把正在運行的常規線程設置爲守護線程。
  3. 在Daemon線程中產生的新線程也是Daemon的
    4.守護線程中不能做重要的操作,因爲它隨時可能中斷
  • 例子
      Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                Thread daemonThread = new Thread(() -> {

                    while (true){
                        System.out.print("我是守護線程" + "\n");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                    }


                });
                //設置爲守護線程
                daemonThread.setDaemon(true);
                daemonThread.start();
                
                System.out.print("開始運行了" + "\n");


            }
        });
        
        thread.start();
//正常來說daemonThread會一直循環
//當時當他設置爲守護線程後 thread執行結束後 daemonThread也隨之結束。

六、resume()、suspend()、stop()

resume():重啓線程
suspend():掛起線程
stop():停止線程
這三個方法都可能會出現一些不可預料的問題, 所以被廢棄了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章