Demo 代码:

public class WaitNotifyDemo {
    private static boolean flag = true;
    private static final Object lock = new Object();

    public static void main(String[] args) throws Exception {
        Thread waitThread = new Thread(new Wait(), "Wait Thread");
        waitThread.start();
        Thread.sleep(1000);
        Thread notifyThread = new Thread(new Notify(), "Notify Thread");
        notifyThread.start();
    }

    private static class Wait implements Runnable {
        @Override
        public void run() {
            synchronized (lock) {
                while (flag) {
                    try {
                        System.out.println(Thread.currentThread() + " flag is true. wait @ " +
                                new SimpleDateFormat("HH:mm:ss").format(new Date()));
                        lock.wait();    // wait thread 由于 Object.wait() 被阻塞,并释放 monitor
                    } catch (InterruptedException e) {
                        System.out.println(e.getMessage());
                    }
                }
                System.out.println(Thread.currentThread() + " flag is false. running @ " +
                        new SimpleDateFormat("HH:mm:ss").format(new Date()));
            }
        }
    }

    private static class Notify implements Runnable {
        @Override
        public void run() {
            synchronized (lock) {
                System.out.println(Thread.currentThread() + " hold lock. notify @ " +
                        new SimpleDateFormat("HH:mm:ss").format(new Date()));
//                lock.notifyAll();
                lock.notify();
                flag = false;
                try {
                    Thread.sleep(5000); // Thread.sleep() 不会释放锁
                } catch (InterruptedException e) {
                    System.out.println(e.getMessage());
                }
            }
            synchronized (lock) {
                System.out.println(Thread.currentThread() + " hold lock again. sleep @ " +
                        new SimpleDateFormat("HH:mm:ss").format(new Date()));
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    System.out.println(e.getMessage());
                }
            }
        }
    }
}

输出信息:

Thread[Wait Thread,5,main] flag is true. wait @ 14:51:52
Thread[Notify Thread,5,main] hold lock. notify @ 14:51:53
Thread[Notify Thread,5,main] hold lock again. sleep @ 14:51:58
Thread[Wait Thread,5,main] flag is false. running @ 14:52:03

Process finished with exit code 0

由以上 Demo 可以知道

  • Object.wait() 方法会阻塞线程,并释放锁
  • Thread.sleep() 方法会阻塞线程,但不会释放锁,被阻塞的线程将继续持有锁
  • Object.notify()、Object.notifyAll() 方法会唤醒被阻塞的线程,notify 随机唤醒一个,notifyAll 唤醒所有

尝试一下 Object.wait(timeout) 和 Thread.join() 方法

public class WaitNotifyDemo {
    private static boolean flag = true;
    private static final Object lock = new Object();

    public static void main(String[] args) throws Exception {
        Thread waitThread = new Thread(new Wait(), "Wait Thread");
        waitThread.start();
        Thread.sleep(1000);
        Thread notifyThread = new Thread(new Notify(), "Notify Thread");
        notifyThread.start();
        waitThread.join();
        System.out.println("join test");
    }

    private static class Wait implements Runnable {
        @Override
        public void run() {
            synchronized (lock) {
                while (flag) {
                    try {
                        System.out.println(Thread.currentThread() + " flag is true. wait @ " +
                                new SimpleDateFormat("HH:mm:ss").format(new Date()));
                        lock.wait(300);    // wait thread 由于 Object.wait() 被阻塞,并释放 monitor
                    } catch (InterruptedException e) {
                        System.out.println(e.getMessage());
                    }
                }
                System.out.println(Thread.currentThread() + " flag is false. running @ " +
                        new SimpleDateFormat("HH:mm:ss").format(new Date()));
            }
        }
    }

    private static class Notify implements Runnable {
        @Override
        public void run() {
            synchronized (lock) {
                System.out.println(Thread.currentThread() + " hold lock. notify @ " +
                        new SimpleDateFormat("HH:mm:ss").format(new Date()));
//                lock.notifyAll();
                lock.notify();
                flag = false;
                try {
                    Thread.sleep(5000); // Thread.sleep() 不会释放锁
                } catch (InterruptedException e) {
                    System.out.println(e.getMessage());
                }
            }
            synchronized (lock) {
                System.out.println(Thread.currentThread() + " hold lock again. sleep @ " +
                        new SimpleDateFormat("HH:mm:ss").format(new Date()));
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    System.out.println(e.getMessage());
                }
            }
        }
    }
}

输出:

Thread[Wait Thread,5,main] flag is true. wait @ 15:04:54
Thread[Wait Thread,5,main] flag is true. wait @ 15:04:55
Thread[Wait Thread,5,main] flag is true. wait @ 15:04:55
Thread[Wait Thread,5,main] flag is true. wait @ 15:04:55
Thread[Notify Thread,5,main] hold lock. notify @ 15:04:55
Thread[Notify Thread,5,main] hold lock again. sleep @ 15:05:00
Thread[Wait Thread,5,main] flag is false. running @ 15:05:05
join test

JDK 源码中关于 Thread.join() 的注释如下:

/**
     * Waits for this thread to die.
     *
     * <p> An invocation of this method behaves in exactly the same
     * way as the invocation
     *
     * <blockquote>
     * {@linkplain #join(long) join}{@code (0)}
     * </blockquote>
     *
     * @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.
     */

在 Demo 的代码中,在 main 方法(主线程) 中调用 waitThread.join() 将会导致 主线程阻塞,等待 waitThread 结束后,继续执行


参考资料:

1、《Java 并发编程艺术》
2、JDK 源码