java併發編程(十)synchronized原理之wait/notify 一、爲什麼需要wait/notify? 二、wait/notify原理 三、wait/notify使用

一、爲什麼需要wait/notify?

我們都知道wati/notify用於線程間的同步。

假設有一把鎖lock,此時線程t1來持有這把鎖,但是由於其不滿足業務條件暫時不能繼續執行,如果t2此時來獲取鎖,發現鎖仍被t1佔有,則會發生阻塞,造成嚴重的效率問題,甚至出現死鎖。

通過wait/notify,可是是t1先進入WAITING狀態,等待t2的喚醒,被喚醒後再去執行對應的操作,這樣就避免了t2線程的阻塞,且t1、t2之間的競爭也沒有了,大大提升執行效率。

二、wait/notify原理

前面我們講過Monitor(管程)的結構,其中有一塊叫做waitSet的區域,裏面存放的是狀態爲WAITING狀態的線程。如下圖虛線框中的內容:

涉及到Monitor,相信大家能夠知道,只有在重量級鎖中,纔會有wait/notify。

下面按照上圖簡單分析下其流程:

  • 假設線程thread1持有了重量級鎖,即Owner,但是此時沒有準備好,所以調用了wait方法。此時其狀態就會變成WAITING,進入到Monitor的WaitSet當中。
  • EntryList和WaitSet當中的線程,都是不佔用CPU時間片的。
  • EntryList當中的線程,會等待Owner執行完成後被喚醒。
  • WaitSet當中的線程,會等待Owner執行notify或notifyAll方法進行喚醒,但是並不是直接獲取到鎖,而是被添加到EntryList當中進行非公平的競爭。

三、wait/notify使用

3.1 常用方法

  • obj.wait() 讓當前持有鎖的Owner進入Monitor的WaitSet進行等待。
  • obj.wait(long timeout) 讓進入 object 監視器的線程到 waitSet 等待,可制定等待超時時間。
  • obj.notify() Owner從正在 waitSet 等待的線程中挑一個喚醒。
  • obj.notifyAll() Owner讓正在 waitSet 等待的線程全部喚醒。

以上方法都是Object類的方法,必須要獲取該對象鎖,才能調用上述方法。

通過下面的例子,演示用法,媽媽正在做飯,爸爸和小A等着喫飯,爸爸發現飯沒有做好,要去睡一會;小A發現飯沒有做好,要去玩一會兒。媽媽做好飯了,叫小A和爸爸喫飯。飯後,小A去寫作業了,爸爸去釣魚了:

    /**
     * 吃了嗎
     */
    static boolean EATEN_YET = false;

    static Object lock = new Object();

    public static void main(String[] args) {

        new Thread(() -> {
            synchronized (lock) {
                while (true) {
                    if (EATEN_YET) {
                        System.out.println(Thread.currentThread().getName() + ":我要開始寫作業了!!");
                        break;
                    } else {
                        try {
                            System.out.println(Thread.currentThread().getName() + ":喫飯前我先玩一會兒!!");
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

        }, "小A").start();

        new Thread(() -> {
            synchronized (lock) {
                while (true) {
                    if (EATEN_YET) {
                        System.out.println(Thread.currentThread().getName() + ":我要去釣魚了!!");
                        break;
                    } else {
                        try {
                            System.out.println(Thread.currentThread().getName() + ":喫飯前我先睡一會兒!!");
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

        }, "爸爸").start();

        new Thread(() -> {
            synchronized (lock) {
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":開飯了!!");
                EATEN_YET = true;
                lock.notifyAll();
            }

        }, "媽媽").start();
    }

結果:

小A:喫飯前我先玩一會兒!!
爸爸:喫飯前我先睡一會兒!!
媽媽:開飯了!!
爸爸:我要去釣魚了!!
小A:我要開始寫作業了!!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章