java中的wait和notify

首先我們需要再複習一下java的synchronized機制,每一個對象都有一個監聽器(monitor),每一個synchronizad()代碼塊的括號裏面的都是一個對象, 當一個線程試圖進入synchronizad的代碼塊的時候,會試圖得到這個對象的monitor,其他處於同一 monitor下面的線程想要進入synchronizad代碼塊的時候就必須等待這個線程釋放monitor,當然,一個線程執行完synchronizad代碼塊後會自動釋放monitor。

Synchronization is achieved using monitors. Every object can have a monitor associated with it, so any object can synchronize blocks. Before a synchronized block can be entered, a thread needs to gain ownership of the monitor for that block. Once the thread has gained ownership of the monitor, no other thread synchronized on the same monitor can gain entry to that block (or any other block or method synchronized on the same monitor). The thread owning the monitor gets to execute all the statements in the block, and then automatically releases ownership of the monitor on exiting the block. At that point, another thread waiting to enter the block can acquire ownership of the monitor.


好了,有了上面的鋪墊,我們可以開始討論 java 裏面的 wait 和 notify 方法了,先來看看java doc


翻譯一下,


讓調用wait方法的線程處於等待狀態並且釋放這個對象的monitor,直到其他線程調用了 notify或者notifyAll方法,當然前提是當前線程必須擁有這個對象的monitor,這就是爲什麼wait方法只能在synchronized裏面被調用



/**
     * Causes the current thread to wait until another thread invokes the
     * {@link java.lang.Object#notify()} method or the
     * {@link java.lang.Object#notifyAll()} method for this object.
     * In other words, this method behaves exactly as if it simply
     * performs the call {@code wait(0)}.
     * <p>
     * The current thread must own this object's monitor. The thread
     * releases ownership of this monitor and waits until another thread
     * notifies threads waiting on this object's monitor to wake up
     * either through a call to the {@code notify} method or the
     * {@code notifyAll} method. The thread then waits until it can
     * re-obtain ownership of the monitor and resumes execution.
     * <p>
     * As in the one argument version, interrupts and spurious wakeups are
     * possible, and this method should always be used in a loop:
     * <pre>
     *     synchronized (obj) {
     *         while (&lt;condition does not hold&gt;)
     *             obj.wait();
     *         ... // Perform action appropriate to condition
     *     }
     * </pre>
     * This method should only be called by a thread that is the owner
     * of this object's monitor. See the {@code notify} method for a
     * description of the ways in which a thread can become the owner of
     * a monitor.
     *
     * @exception  IllegalMonitorStateException  if the current thread is not
     *               the owner of the object's monitor.
     * @exception  InterruptedException if any thread interrupted the
     *             current thread before or while the current thread
     *             was waiting for a notification.  The <i>interrupted
     *             status</i> of the current thread is cleared when
     *             this exception is thrown.
     * @see        java.lang.Object#notify()
     * @see        java.lang.Object#notifyAll()
     */
    public final void wait() throws InterruptedException {
        wait(0);
    }




再看notify方法,喚醒一個正在等待這個對象monitor的線程,如果有多個線程在等待,就隨機選擇一個,(用notifyAll可以都喚醒),而且這個方法也只能在當前線程必須擁有這個對象的monitor的時候調用(也就是synchronized裏面),而且,被喚醒的線程只有在這個線程跑完後纔會執行



/**
    * Wakes up a single thread that is waiting on this object's
    * monitor. If any threads are waiting on this object, one of them
    * is chosen to be awakened. The choice is arbitrary and occurs at
    * the discretion of the implementation. A thread waits on an object's
    * monitor by calling one of the {@code wait} methods.
    * <p>
    * The awakened thread will not be able to proceed until the current
    * thread relinquishes the lock on this object. The awakened thread will
    * compete in the usual manner with any other threads that might be
    * actively competing to synchronize on this object; for example, the
    * awakened thread enjoys no reliable privilege or disadvantage in being
    * the next thread to lock this object.
    * <p>
    * This method should only be called by a thread that is the owner
    * of this object's monitor. A thread becomes the owner of the
    * object's monitor in one of three ways:
    * <ul>
    * <li>By executing a synchronized instance method of that object.
    * <li>By executing the body of a {@code synchronized} statement
    *     that synchronizes on the object.
    * <li>For objects of type {@code Class,} by executing a
    *     synchronized static method of that class.
    * </ul>
    * <p>
    * Only one thread at a time can own an object's monitor.
    *
    * @exception  IllegalMonitorStateException  if the current thread is not
    *               the owner of this object's monitor.
    * @see        java.lang.Object#notifyAll()
    * @see        java.lang.Object#wait()
    */
   public final native void notify();


再看一個例子,讓兩個線程輪流打印,一個線程打印另一個線程等待


public class Test {
        private static int n = 0;
        private static boolean flag = false;
        public static void main(String[] args) {
                final Object lock = new Object();
                Thread thread1 = new Thread(new Runnable() {
                        @Override
                        public void run() {
                                synchronized (lock) {
                                        while (true) {
                                                if (flag != true) {// 1
                                                        try {
                                                            System.out
                                                                    .println("before thread1 wait");
                                                                lock.wait();
                                                            System.out
                                                                .println("after thread1 wait");
                                                        } catch (InterruptedException e) {
                                                                e.printStackTrace();
                                                        }
                                                }
                                                for (int i = 0; i < 5; i++) {
                                                        System.out.println(n++);
                                                        try {
                                                                Thread.sleep(500);
                                                        } catch (InterruptedException e) {
                                                                e.printStackTrace();
                                                        }
                                                }
                                                flag = false;
                                                lock.notify();//雖然在這裏就調用了,但是必須等
                                                try {
                                                    Thread.sleep(5000);
                                                } catch (InterruptedException e) {
                                                    e.printStackTrace();
                                                }
                                                System.out
                                                        .println("after thread1 notify");
                                        }
                                }
                        }
                });
                                                                                                                                                                            
                Thread thread2 = new Thread(new Runnable() {
                        @Override
                        public void run() {
                                synchronized (lock) {
                                        while (true) {
                                                if (flag != false) {
                                                        try {
                                                            System.out
                                                            .println("before thread2 wait");
                                                                lock.wait();
                                                            System.out
                                                                .println("after thread2 wait");
                                                        } catch (InterruptedException e) {
                                                                e.printStackTrace();
                                                        }
                                                }
                                                for (int i = 0; i < 5; i++) {
                                                        System.out.println(n--);
                                                        try {
                                                                Thread.sleep(500);
                                                        } catch (InterruptedException e) {
                                                                e.printStackTrace();
                                                        }
                                                }
                                                flag = true;
                                                lock.notify();//2
                                                try {
                                                    Thread.sleep(5000);
                                                } catch (InterruptedException e) {
                                                    e.printStackTrace();
                                                }
                                                System.out
                                                        .println("after thread2 notify");
                                        }
                                }
                        }
                });
                                                                                                                                                                            
                thread1.start();
                thread2.start();
        }
}



下面是打印結果


before thread1 wait
0
-1
-2
-3
-4
after thread2 notify
before thread2 wait
after thread1 wait
-5
-4
-3
-2
-1
after thread1 notify


第 1 行: thread 1 首先奪取了lock對象的monitor,進入run方法執行,但是 flag = false,所以調用了 wait方法,交出monitor的所有權

2 - 6 行: thread 2 得到monitor開始執行循環打印

7 - 8 行: thread 2 雖然在打印之後就調用了 notify,但是被喚醒的線程只有在當前線程放棄鎖之後才執行,所以thread 2 仍然在執行,直到調用了 wait 方法

第 9 行: thread 1 終於得到了thread 2 放棄了 monitor,繼續執行

10- 14行: thread 1 循環打印

第 15 行: thread 1 繼續執行,直到wait.....




這裏補充一下 Thread.sleep方法的說明。先看註釋:


/**
    * Causes the currently executing thread to sleep (temporarily cease
    * execution) for the specified number of milliseconds, subject to
    * the precision and accuracy of system timers and schedulers. The thread
    * does not lose ownership of any monitors.
    *
    * @param  millis
    *         the length of time to sleep in milliseconds
    *
    * @throws  IllegalArgumentException
    *          if the value of {@code millis} is negative
    *
    * @throws  InterruptedException
    *          if any thread has interrupted the current thread. The
    *          <i>interrupted status</i> of the current thread is
    *          cleared when this exception is thrown.
    */
   public static native void sleep(long millis) throws InterruptedException;



讓當前線程休息一段時間,時間可以指定,重點是,當前線程並沒有釋放 monitor 的所有權


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