4. 線程Thread

線程狀態

java中線程的狀態分爲6種:

  1. 初始化(New):當線程完成對象創建後,即new Thread(...),還沒有調用start()方法之前,線程處於此狀態。
  2. 運行態(Runnable):調用start方法,正在jvm中運行,但是可能正在等待操作系統的其他資源。(包含了操作系統的可運行和運行中狀態)
  3. 阻塞(Blocked):受阻塞,並且正在等待監視器鎖。
  4. 等待(Waiting):處於等待狀態的線程,正在等待另一個線程執行特定的操作
  5. 超時等待(Timed_Waiting):該狀態與等待(Waiting)的不同點在於,它可以在指定時間後自行返回。
  6. 終止(Terminated):線程執行完成或執行過程中出現異常時處於此狀態。
     

操作系統中線程的狀態分爲5種:

  1. 初始狀態:線程剛被創建,還未被分配 CPU 
  2. 可運行狀態(Ready):線程等待系統分配 CPU ,從而執行任務。
  3. 運行中(Running):操作系統將 CPU 分配給線程,線程執行任務。
  4. 休眠狀態:線程讓出CPU使用權,進入休眠狀態。休眠結束,線程狀態將會先變成可運行狀態。(包含了java中的阻塞、等待、超時等待狀態)
  5. 終止狀態:線程執行結束或者執行過程發生異常將會使線程進入終止狀態,這個狀態下線程使命已經結束。

狀態轉換圖

Wait/Notify通知機制

wait()方法可以讓線程進入等待狀態,而notify()可以使等待的狀態喚醒。這樣的同步機制十分適合生產者、消費者模式:消費者消費某個資源,而生產者生產該資源。當該資源缺失時,消費者調用wait()方法進行自我阻塞,等待生產者的生產;生產者生產完畢後調用notify/notifyAll()喚醒消費者進行消費。

//消費者線程
static class Consume implements Runnable {
    @Override
    public void run() {
        synchronized (obj) {
            System.out.println("消費者:進入線程");
            System.out.println("消費者:wait flag 1:" + flag);
			//判斷條件是否滿足,若不滿足則等待
            while (!flag) {  
                try {
                    System.out.println("消費者:還沒生產,進入等待");
                    obj.wait();
                    System.out.println("消費者:結束等待");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("消費者:wait flag 2:" + flag);
            System.out.println("消費者:開始消費");
            System.out.println("消費者:結束消費");
        }

    }
}


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

    Thread consume = new Thread(new Consume(), "Consume");
    Thread produce = new Thread(new Produce(), "Produce");
	//讓消費者先消費,再啓動生產者
    consume.start();
    Thread.sleep(1000);
    produce.start();

    try {
        produce.join();
        consume.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

上述用例執行結果如下:

消費者:進入線程
消費者:wait flag 1:false
消費者:還沒生產,進入等待
生產者:進入生產者線程
生產者:開始生產
生產者:結束生產
消費者:結束等待
消費者:wait flag 2:true
消費者:開始消費
消費者:結束消費

從結果的輸出可以得到以下結論。

  1. wait函數讓消費者陷入等待階段,然後生產者生產完成後調用notify發出通知,而消費者接到通知後並沒有立刻進行消費,因爲生產者在這裏仍然持有鎖,直到生產者完全退出代碼塊,消費者纔開始消費。

  2. 消費者被喚醒時線程並不是由等待阻塞直接到運行態,而是先轉到同步阻塞,由等待隊列轉移到阻塞隊列,等到拿到對象的鎖纔會執行(畢竟synchronized同步方法(塊)同一時刻只允許一個線程在裏面)。

  3. 消費者執行時是繼續執行而不是重新進入同步代碼塊。

Wait/Notify使用條件

那是不是不用synchronized,就可以達到消費者被喚醒後就立馬消費呢?
將上述鎖住代碼塊的synchronized代碼刪除再運行發現報錯 java.lang.IllegalMonitorStateException,JDK對該異常的描述如下:線程試圖等待對象的監視器或者試圖通知其他正在等待對象監視器的線程,但本身沒有對應的監視器的所有權。
這是因爲調用wait方法並沒有獲取到對象的monitor所有權,而synchronized底層原理就是通過給對象添加monitorenter來完成鎖功能。
因此Wait/Notify機制只能在synchronized代碼塊中使用。

發佈了23 篇原創文章 · 獲贊 5 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章