Java多線程——wait、notify、notifyAll用法

什麼是wait、notify和notifyAll

  • waitnotifynotifyAll都是是Object的方法,任何一個繼承Object類的對象都可以調用
  • 所有的操作都是針對某個特定的對象來說的,比如對象的引用變了,那麼就相當於waitnotify作用在了兩個不同的對象上,會拋出IllegalMonitorStateException
  • 這三個方法只有在當前線程獲取到對象的Monitor(可以理解爲鎖)以後纔可以調用
  • 同一時間只有一個線程可以獲得對象的Monitor
  • 當前線程獲取對象的Monitor有三種方式
    • 通過執行這個對象的synchronized實例方法
    • 通過執行以這個對象作爲同步鎖的synchronized方法體
    • 對於Class類型的對象,執行一個該類型的synchronized靜態方法
  • wait調用後,會讓線程進入等待狀態,知道另外一個線程喚醒它,並釋放對象的Monitor(釋放鎖)
  • notify調用後,會喚醒等待當前對象Monitor的一個線程,具體哪一個不好說,由JVM控制
  • notifyAll調用後,會喚醒所有等待當前對象Monitor的線程
  • 被喚醒的線程不會立刻運行,而是會等待持有對象Monitor的線程將Monitor(鎖)釋放掉。在Monitor被釋放掉以後,所有被喚醒的線程,將會重新競爭這個鎖,獲取到鎖的線程纔會執行wait的下一條語句
  • 官方文檔建議將wait放到一個循環中,因爲在多線程的環境下,共享對象的狀態隨時可能改變,所以通過while循環的條件判斷,當滿足條件的情況下,再觸發wait

什麼時候釋放鎖?

由於等待鎖的線程只有在獲得這把鎖之後,才能恢復運行,所以讓持有鎖的線程在不需要鎖的時候及時釋放鎖是很重要的。
在以下情況下,持有鎖的線程會釋放鎖:

  1. 執行完同步代碼塊。
  2. 在執行同步代碼塊的過程中,遇到異常而導致線程終止。
  3. 在執行同步代碼塊的過程中,執行了鎖所屬對象的wait()方法,這個線程會釋放鎖,進行對象的等待池。

除了以上情況外,只要持有鎖的線程還沒有執行完同步代碼塊,就不會釋放鎖。
因此在以下情況下,線程不會釋放鎖:

  1. 在執行同步代碼塊的過程中,執行了Thread.sleep()方法,當前線程放棄CPU,開始睡眠,在睡眠中不會釋放鎖。
  2. 在執行同步代碼塊的過程中,執行了Thread.yield()方法,當前線程放棄CPU,但不會釋放鎖。
  3. 在執行同步代碼塊的過程中,其他線程執行了當前對象的suspend()方法,當前線程被暫停,但不會釋放鎖。但Thread類的suspend()方法已經被廢棄。

三種獲取Monitor的方式

  1. 對象,鎖爲對象
public class WaitNotifyMonitorObjectDemo {

    private static Object o = new Object();

    private static boolean flag = true;

    static final class A extends Thread {
        @Override
        public void run() {
            synchronized (o) {
                while (flag) {
                    try {
                        System.out.println("ready to wait");
                        o.wait();
                        System.out.println("i am back");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                flag = true;
            }
        }
    }

    static final class B extends Thread {
        @Override
        public void run() {
            synchronized (o) {
                o.notify();
                System.out.println("wake up o");
                flag = false;
            }
        }
    }

    public static void main(String[] args) {
        A a = new A();
        a.start();
        B b = new B();
        b.start();
    }
}
執行結果
ready to wait
i am back
ready to wait
  1. 同步方法,鎖爲方法所在對象
public class WaitNotifySyncMethodDemo {

    private static boolean flag = true;

    static class A {
        synchronized void method1() {
            while (flag) {
                try {
                    System.out.println("ready to wait");
                    this.wait();
                    System.out.println("i am back");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            flag = true;
        }

        synchronized void method2() {
            this.notify();
        }
    }

    public static void main(String[] args) {
        final A a = new A();
        new Thread(a::method1).start();
        new Thread(a::method2).start();
    }
}
執行結果
ready to wait
i am back
ready to wait
  1. 同步靜態方法,鎖爲類對象
public class WaitNotifySyncStaticMethodDemo {

    private static boolean flag = true;

    private synchronized static void method1() {
        while (flag) {
            try {
                System.out.println("ready to wait");
                WaitNotifySyncStaticMethodDemo.class.wait();
                System.out.println("i am back");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        flag = true;
    }

    private synchronized static void method2() {
        WaitNotifySyncStaticMethodDemo.class.notify();
    }

    public static void main(String[] args) {
        new Thread(WaitNotifySyncStaticMethodDemo::method1).start();
        new Thread(WaitNotifySyncStaticMethodDemo::method2).start();
    }
}
執行結果
ready to wait
i am back
ready to wait

notify和notifyAll的區別示例

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