首先我们需要再复习一下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 的所有权