wait、notify、notifyAll 的用法

wait()、notify()、notifyAll()是三個定義在Object類裏的方法,用來控制線程的狀態,通常與synchronized合用。這三個方法最終調用的都是jvm的native方法, 隨着jvm運行平臺的不一樣可能有些許差別。java

當線程執行wait()方法時候,會釋放當前對象的控制權,而後讓出CPU,進入等待狀態;
只有當 notify/notifyAll() 被執行時候,纔會喚醒一個或多個正處於等待狀態的線程(並不會馬上釋放控制權),而後繼續往下執行,直到執行完synchronized 代碼塊的代碼或是中途遇到wait() ,再次釋放鎖
多線程


wait() 須要被try catch包圍,拋出InterruptedException; 中斷也可使wait等待的線程喚醒。
notify方法只喚醒一個等待對象的線程並使該線程開始執行。因此若是有多個線程等待一個對象,這個方法只會喚醒其中一個線程,選擇哪一個線程取決於操做系統對多線程管理的實現。
notifyAll 會喚醒全部等待對象的線程,哪個線程將會第一個處理取決於操做系統的實現。jvm

要注意的是:spa

  1. 任何一個時刻,對象的控制權(monitor)只能被一個線程擁有。
  2. 不管是執行對象的wait、notify仍是notifyAll方法,必須保證當前運行的線程取得了該對象的控制權(monitor)
  3. 若是在沒有控制權的線程裏執行對象的以上三種方法,報java.lang.IllegalMonitorStateException異常。
  4. JVM基於多線程,默認狀況下不能保證運行時線程的時序性

eg.1操作系統

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class SyncTest {
    public static void main(String[] args) {
        Object obj = new Object();
        Thread thread1 = new Thread(() -> {
            synchronized (obj) {
                try {
                    log.info("1-before wait");
                    obj.wait();
                    log.info("1-after wait");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        });

        Thread thread2 = new Thread(() -> {
            synchronized (obj) {
                try {
                    log.info("2-before wait");
                    obj.wait();
                    log.info("2-after wait");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        });
        Thread thread3 = new Thread(() -> {
            synchronized (obj) {
                try {
                    log.info("3-before notifyAll");
                    obj.notifyAll();
                    try {
                        Thread.currentThread().sleep(2000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    log.info("3-after notifyAll");

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            try {
                Thread.currentThread().sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            log.info("3-out synchronized");

        });

        thread1.start();
        thread2.start();

        try {
            Thread.currentThread().sleep(2000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
        thread3.start();

    }
}

結果: 線程3在sleep前,已經notifyall。但在sleep的時間中,線程1和線程2並無繼續執行。.net

2018-10-23 16:15:22,897 [Thread-1] INFO   [SyncTest] [SyncTest.java:24]   2-before wait
2018-10-23 16:15:22,906 [Thread-0] INFO   [SyncTest] [SyncTest.java:11]   1-before wait
2018-10-23 16:15:23,896 [Thread-2] INFO   [SyncTest] [SyncTest.java:43]   3-before notifyAll
2018-10-23 16:15:25,896 [Thread-2] INFO   [SyncTest] [SyncTest.java:50]   3-after notifyAll
2018-10-23 16:15:25,898 [Thread-0] INFO   [SyncTest] [SyncTest.java:13]   1-after wait
2018-10-23 16:15:25,898 [Thread-1] INFO   [SyncTest] [SyncTest.java:26]   2-after wait
2018-10-23 16:15:27,897 [Thread-2] INFO   [SyncTest] [SyncTest.java:61]   3-out synchronized

驗證:擁有控制權的線程notify在結束執行synchronized塊以前時,控制權不會釋放!線程

eg.2code

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class SyncTest {
    public static void main(String[] args) {
        Object obj = new Object();
        Thread thread1 = new Thread(() -> {
            synchronized (obj) {
                try {
                    log.info("1-before wait");
                    obj.wait();
                    log.info("1-after wait");
                } catch (Exception e) {
                    log.info("1-catch {}. interrupt狀態: {}", e.toString(), Thread.currentThread().isInterrupted());
                    Thread.currentThread().interrupt();
                    log.info("設置標誌位後interrupt狀態: {}", Thread.currentThread().isInterrupted());

                }
            }
        });

        thread1.start();
        try {
            Thread.currentThread().sleep(2000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
        log.info("線程初始interrupt狀態:{}", thread1.isInterrupted());
        thread1.interrupt();

    }
}

結果: 當線程調用interrupt()時,wait()拋出的InterruptedException被線程捕獲。對象

2018-10-23 17:13:38,047 [Thread-0] INFO   [SyncTest] [SyncTest.java:10]   1-before wait
2018-10-23 17:13:40,042 [main] INFO   [SyncTest] [SyncTest.java:28]   線程初始interrupt狀態:false
2018-10-23 17:13:40,044 [Thread-0] INFO   [SyncTest] [SyncTest.java:14]   1-catch java.lang.InterruptedException. interrupt狀態: false
2018-10-23 17:13:40,044 [Thread-0] INFO   [SyncTest] [SyncTest.java:16]   設置標誌位後interrupt狀態: true

驗證:Thread.currentThread().interrupt() 與 thread1.interrupt() 做用類似, 將中斷標記位設置爲true。 拋出的異常被捕獲後,中斷標示會被清除!blog

 

wait與notifyAll實現簡單的生產者與消費者

相關文章
相關標籤/搜索