Object.wait以及notify注解

wait()

* Causes the calling thread to wait until another thread calls the {@code
     * notify()} or {@code notifyAll()} method of this object. This method can
     * only be invoked by a thread which owns this object's monitor; see
     * {@link #notify()} on how a thread can become the owner of a monitor.
     * <p>
     * A waiting thread can be sent {@code interrupt()} to cause it to
     * prematurely stop waiting, so {@code wait} should be called in a loop to
     * check that the condition that has been waited for has been met before
     * continuing.
     * <p>
     * While the thread waits, it gives up ownership of this object's monitor.
     * When it is notified (or interrupted), it re-acquires the monitor before
     * it starts running.
     * @throws IllegalMonitorStateException
     *             if the thread calling this method is not the owner of this
     *             object's monitor.
     * @throws InterruptedException
     *             if another thread interrupts this thread while it is waiting.

該方法的注釋說:使調用線程處于wait狀態,直到其他線程調用這個Object的notify或者notifyAll才會被喚醒。這個方法只能被擁有這個Object的Monitor才能被調用。一個正在wait的線程能夠被調用interrupt方法。
當一個線程處于Wait,它放棄了它自己的Object Monitor,當它被notified或者interrupted的時候,它會在它啟動之前重新請求這個monitor

noitify()

* Causes a thread which is waiting on this object's monitor (by means of
* calling one of the {@code wait()} methods) to be woken up. If more than one thread is waiting, one of them is chosen at the discretion of the VM. The chosen thread will not run immediately. The thread
     * that called {@code notify()} has to release the object's monitor first.
     * Also, the chosen thread still has to compete against other threads that
     * try to synchronize on the same object.
     * This method can only be invoked by a thread which owns this object's
     * monitor. A thread becomes owner of an object's monitor by executing a synchronized method of that object; by executing the body of a {@code synchronized} statement that synchronizes on the object;by executing a synchronized static method if the object is of type {@code Class}.

使得一個正在等待這個Object的Monitor的線程被喚醒。如果超過一個線程正在等待的話,那么就只有一個線程會被喚醒,而這個線程會由VM自己決定。而這個被選擇的線程并不會立馬就進入run的狀態,調用了notify的線程會首先釋放這個Object的Monitor。并且,被選擇的線程必須完成和其他線程完成對這個Object鎖的競爭。這個方法只能被擁有這個Object的Monitor的線程調用。這個線程擁有這個Object的Monitor,通過執行一個同步方法或者一個同步的代碼塊來獲取這個對象的鎖,或者通過執行這個對象的Class類來進行同步。

簡單來說,也就是在使用wait和notify的時候,需要使用synchoronized代碼塊將對象進行Monitor操作,這個操作可以是一個同步代碼塊,也可以是一個同步的方法,也可以用一個class對象進行同步,總之,在調用wait和notify的時候,必須要進行同步。并且在有多個線程處于wait狀態的時候,當調用notify的時候,只有一個線程會收到這個消息,如果是notifyAll的話,所有的線程都會進行競爭,最后也只會有一個線程能夠獲取到資源,但是它也不會立馬進行到run的狀態,而是進入就緒的狀態,等待時間片到它的時候,就可以執行了。而這個線程的選擇是靠JVM來自主決定的。

下面舉一個例子:

  1. 錯誤的例子:使用wait和noitify的時候沒有加同步代碼塊

    public class Test {
    public static void main(String[] args) {
          Object lock = new Object();
          ThreadA threadA = new ThreadA(lock);
          ThreadB threadB = new ThreadB(lock);
          threadA.start();
          threadB.start();
    }
    private static class ThreadA extends Thread {
          Object obj;
    
        public ThreadA(Object lock) {
            obj = lock;
            setName("ThreadA");
        }
    
        @Override
        public void run() {
            super.run();
            System.out.println("Start Wait:" + Thread.currentThread().getName());
            try {
                obj.wait();
            } catch (InterruptedException e) {
            e.printStackTrace();
            }
            System.out.println("Wait End:" + Thread.currentThread().getName());
        }
    }
    
    private static class ThreadB extends Thread {
        Object obj;
    
    public ThreadB(Object lock) {
        obj = lock;
        setName("ThreadB");
    }
    
        @Override
        public void run() {
            super.run();
            System.out.println("ThreadB Start:" + Thread.currentThread().getName());
            try {
                Thread.sleep(5000L);
            } catch (InterruptedException e) {
            }
            System.out.println("After ThreadB Sleep 5S");
            obj.notify();
            System.out.println("ThreadB notify:" + Thread.currentThread().getName());
            }
        }
     }
    

運行后的結果為:

代碼1運行結果
  1. 正確使用wait notify的例子:

    public class Test {
        public static void main(String[] args) {
            Object lock = new Object();
            ThreadA threadA = new ThreadA(lock);
            ThreadB threadB = new ThreadB(lock);
            threadA.start();
            threadB.start();
    }
    
    private static class ThreadA extends Thread {
        Object obj;
    
        public ThreadA(Object lock) {
            obj = lock;
            setName("ThreadA");
        }
    
        @Override
        public void run() {
            super.run();
            System.out.println("Start Wait:" + Thread.currentThread().getName());
            try {
                synchronized (obj) {
                    obj.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Wait End:" + Thread.currentThread().getName());
        }
    }
    
        private static class ThreadB extends Thread {
            Object obj;
    
            public ThreadB(Object lock) {
                obj = lock;
                setName("ThreadB");
            }
    
        @Override
        public void run() {
            super.run();
            System.out.println("ThreadB Start:" + Thread.currentThread().getName());
            try {
                Thread.sleep(5000L);
            } catch (InterruptedException e) {
            }
            System.out.println("After ThreadB Sleep 5S");
            synchronized (obj) {
                obj.notify();
            }
            System.out.println("ThreadB notify:" + Thread.currentThread().getName());
            }
        }
    }
    

運行結果:


示例2運行結果

從正確的結果可以看出,在ThreadA和ThreadB同時啟動的時候,ThreadB先運行,然后進入了Sleep,后ThreadA運行,打印出了StartWait,然后處于wait狀態,等到ThreadB從Sleep5秒醒來后,調用object.notify,并且打印ThreadB notify,于是通知ThreadA,接著ThreadA獲取到了Object的Monitor之后,結束運行。

2.錯誤的例子
讓B線程先啟動,并且在B線程執行完之后,再繼續執行主線程,之后再啟動A線程,此時A線程中會調用wait方法,這時候線程A一直在等待notify而導致程序無法正常結束。

public class Test {
public static void main(String[] args) {
    Object lock = new Object();
    ThreadA threadA = new ThreadA(lock);
    ThreadB threadB = new ThreadB(lock);
    threadB.start();
    try {
        threadB.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    threadA.start();
}

private static class ThreadA extends Thread {
    Object obj;

    public ThreadA(Object lock) {
        obj = lock;
        setName("ThreadA");
    }

    @Override
    public void run() {
        super.run();
        System.out.println("Start Wait:" + Thread.currentThread().getName());
        try {
            synchronized (obj) {
                obj.wait();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Wait End:" + Thread.currentThread().getName());
    }
}

private static class ThreadB extends Thread {
    Object obj;

    public ThreadB(Object lock) {
        obj = lock;
        setName("ThreadB");
    }

    @Override
    public void run() {
        super.run();
        System.out.println("ThreadB Start:" + Thread.currentThread().getName());
        try {
            Thread.sleep(5000L);
        } catch (InterruptedException e) {
        }
        System.out.println("After ThreadB Sleep 5S");
        synchronized (obj) {
            obj.notify();
        }
        System.out.println("ThreadB notify:" + Thread.currentThread().getName());
    }
}
}
示例2運行結果
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容