转载--深入理解Java多线程中的wait(),notify()和sleep(),但是最后有自己的问题

大家在学习Java的过程中,势必要进行多线程的系统学习,这部分内容知识对于你在工作中的影响是极大的,并且在面试的过程中,这部分知识也是必然会被问到的。既然多线程的知识如此重要,那么我们就不能浅尝辄止。在这篇文章中,我想通过阅读源码的方式给大家分享一下我自己对于Java中wait(),notify()和sleep()的理解,并且在后面我也做一些内容上的扩展,与大家共同思考。如果在文章中有描述不妥或者逻辑错误的地方,欢迎大家指正,谢谢!

wait()和sleep()

我们先简单的了解一下wait()和sleep()这两个方法:

首先wait()是属于Object类的方法,从源码给出的解释来看,wait()方法可以做到如下几点:

(1)首先,调用了wait()之后会引起当前线程处于等待状状态。

(2)其次,每个线程必须持有该对象的monitor。如果在当前线程中调用wait()方法之后,该线程就会释放monitor的持有对象并让自己处于等待状态。

(3)如果想唤醒一个正在等待的线程,那么需要开启一个线程通过notify()或者notifyAll()方法去通知正在等待的线程获取monitor对象。如此,该线程即可打破等待的状态继续执行代码。

  1. /** 
  2.      * Causes the current thread to wait until another thread invokes the 
  3.      * {@link java.lang.Object#notify()} method or the 
  4.      * {@link java.lang.Object#notifyAll()} method for this object. 
  5.      * In other words, this method behaves exactly as if it simply 
  6.      * performs the call {@code wait(0)}. 
  7.      * <p> 
  8.      * The current thread must own this object’s monitor. The thread 
  9.      * releases ownership of this monitor and waits until another thread 
  10.      * notifies threads waiting on this object’s monitor to wake up 
  11.      * either through a call to the {@code notify} method or the 
  12.      * {@code notifyAll} method. The thread then waits until it can 
  13.      * re-obtain ownership of the monitor and resumes execution. 
  14.      * <p> 
  15.      * As in the one argument version, interrupts and spurious wakeups are 
  16.      * possible, and this method should always be used in a loop: 
  17.      * <pre> 
  18.      *     synchronized (obj) { 
  19.      *         while (<condition does not hold>) 
  20.      *             obj.wait(); 
  21.      *         … // Perform action appropriate to condition 
  22.      *     } 
  23.      * </pre> 
  24.      * This method should only be called by a thread that is the owner 
  25.      * of this object’s monitor. See the {@code notify} method for a 
  26.      * description of the ways in which a thread can become the owner of 
  27.      * a monitor. 
  28.      * 
  29.      * @exception  IllegalMonitorStateException  if the current thread is not 
  30.      *               the owner of the object’s monitor. 
  31.      * @exception  InterruptedException if any thread interrupted the 
  32.      *             current thread before or while the current thread 
  33.      *             was waiting for a notification.  The <i>interrupted 
  34.      *             status</i> of the current thread is cleared when 
  35.      *             this exception is thrown. 
  36.      * @see        java.lang.Object#notify() 
  37.      * @see        java.lang.Object#notifyAll() 
  38.      */  
  39.     public final void wait() throws InterruptedException {  
  40.         wait(0);  
  41.     }  
/**
     * 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 (<condition does not hold>)
     *             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);
    }

代码演示:

  1. public class Main {  
  2.     public static void main(String[] args) {  
  3.         Main main = new Main();  
  4.         main.startThread();  
  5.     }  
  6.   
  7.     /** 
  8.      * 线程锁 
  9.      */  
  10.     private final Object object = new Object();  
  11.   
  12.     /** 
  13.      * 启动线程 
  14.      */  
  15.     public void startThread() {  
  16.         Thread t = new Thread(new Runnable() {  
  17.             @Override  
  18.             public void run() {  
  19.                 System.out.println(”开始执行线程。。。”);  
  20.                 System.out.println(”进入等待状态。。。”);  
  21.                 synchronized (object) {  
  22.                     try {  
  23.                         object.wait();  
  24.                     } catch (InterruptedException e) {  
  25.                         e.printStackTrace();  
  26.                     }  
  27.                 }  
  28.                 System.out.println(”线程结束。。。”);  
  29.             }  
  30.         });  
  31.         t.start();  
  32.     }  
  33. }  
public class Main {
    public static void main(String[] args) {
        Main main = new Main();
        main.startThread();
    }

    /**
     * 线程锁
     */
    private final Object object = new Object();

    /**
     * 启动线程
     */
    public void startThread() {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("开始执行线程。。。");
                System.out.println("进入等待状态。。。");
                synchronized (object) {
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("线程结束。。。");
            }
        });
        t.start();
    }
}

从代码来看,在执行线程和线程结束之间,我们先让该线程获取object对象作为自己的object’s monitor,然后调用了object对象的wait()方法从而让其进入等待状态。那么程序运行的结果如下:


程序在未被唤醒之后,将不再打印“线程结束”,并且程序无法执行完毕一直处于等待状态。


sleep()方法来自于Thread类,从源码给出的解释来看,sleep()方法可以做到如下几点:

(1)首先,调用sleep()之后,会引起当前执行的线程进入暂时中断状态,也即睡眠状态。

(2)其次,虽然当前线程进入了睡眠状态,但是依然持有monitor对象。

(3)在中断完成之后,自动进入唤醒状态从而继续执行代码。

  1. /** 
  2.     * Causes the currently executing thread to sleep (temporarily cease 
  3.     * execution) for the specified number of milliseconds, subject to 
  4.     * the precision and accuracy of system timers and schedulers. The thread 
  5.     * does not lose ownership of any monitors. 
  6.     * 
  7.     * @param  millis 
  8.     *         the length of time to sleep in milliseconds 
  9.     * 
  10.     * @throws  IllegalArgumentException 
  11.     *          if the value of {@code millis} is negative 
  12.     * 
  13.     * @throws  InterruptedException 
  14.     *          if any thread has interrupted the current thread. The 
  15.     *          <i>interrupted status</i> of the current thread is 
  16.     *          cleared when this exception is thrown. 
  17.     */  
  18.    public static native void sleep(long millis) throws InterruptedException;  
 /**
     * 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;


代码演示:

  1. public class Main {  
  2.     public static void main(String[] args) {  
  3.         Main main = new Main();  
  4.         main.startThread();  
  5.     }  
  6.   
  7.     /** 
  8.      * 启动线程 
  9.      */  
  10.     public void startThread() {  
  11.         Thread t = new Thread(new Runnable() {  
  12.             @Override  
  13.             public void run() {  
  14.                 System.out.println(”开始执行线程。。。”);  
  15.                 System.out.println(”进入睡眠状态。。。”);  
  16.                 try {  
  17.                     Thread.sleep(3000);  
  18.                 } catch (InterruptedException e) {  
  19.                     e.printStackTrace();  
  20.                 }  
  21.                 System.out.println(”线程结束。。。”);  
  22.             }  
  23.         });  
  24.         t.start();  
  25.     }  
  26. }  
public class Main {
    public static void main(String[] args) {
        Main main = new Main();
        main.startThread();
    }

    /**
     * 启动线程
     */
    public void startThread() {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("开始执行线程。。。");
                System.out.println("进入睡眠状态。。。");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程结束。。。");
            }
        });
        t.start();
    }
}

从运行的结果来看,我们可以看出程序虽然在运行过程中中断了3秒,但是在3秒结束之后依然会继续执行代码,直到运行结束。在睡眠的期间内,线程会一直持有monitor对象。



那么从以上的理论和实践来分析,我们能得出如下结论:

(1)在线程的运行过程中,调用该线程持有monitor对象的wait()方法时,该线程首先会进入等待状态,并将自己持有的monitor对象释放。

(2)如果一个线程正处于等待状态时,那么唤醒它的办法就是开启一个新的线程,通过notify()或者notifyAll()的方式去唤醒。当然,需要注意的一点就是,必须是同一个monitor对象。

(3)sleep()方法虽然会使线程中断,但是不会将自己的monitor对象释放,在中断结束后,依然能够保持代码继续执行。


notify()和notifyAll()

说完了wait()方法之后,我们接下来讨论一下Object类中的另外两个与wait()相关的方法。首先还是通过源码的方式让大家先初步了解一下:
  1. /** 
  2.      * Wakes up a single thread that is waiting on this object’s 
  3.      * monitor. If any threads are waiting on this object, one of them 
  4.      * is chosen to be awakened. The choice is arbitrary and occurs at 
  5.      * the discretion of the implementation. A thread waits on an object’s 
  6.      * monitor by calling one of the {@code wait} methods. 
  7.      * <p> 
  8.      * The awakened thread will not be able to proceed until the current 
  9.      * thread relinquishes the lock on this object. The awakened thread will 
  10.      * compete in the usual manner with any other threads that might be 
  11.      * actively competing to synchronize on this object; for example, the 
  12.      * awakened thread enjoys no reliable privilege or disadvantage in being 
  13.      * the next thread to lock this object. 
  14.      * <p> 
  15.      * This method should only be called by a thread that is the owner 
  16.      * of this object’s monitor. A thread becomes the owner of the 
  17.      * object’s monitor in one of three ways: 
  18.      * <ul> 
  19.      * <li>By executing a synchronized instance method of that object. 
  20.      * <li>By executing the body of a {@code synchronized} statement 
  21.      *     that synchronizes on the object. 
  22.      * <li>For objects of type {@code Class,} by executing a 
  23.      *     synchronized static method of that class. 
  24.      * </ul> 
  25.      * <p> 
  26.      * Only one thread at a time can own an object’s monitor. 
  27.      * 
  28.      * @exception  IllegalMonitorStateException  if the current thread is not 
  29.      *               the owner of this object’s monitor. 
  30.      * @see        java.lang.Object#notifyAll() 
  31.      * @see        java.lang.Object#wait() 
  32.      */  
  33.     public final native void notify();  
/**
     * 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();
先来看下notify()这个方法,通过阅读源码我们可以总结一下几点:
(1)当一个线程处于wait()状态时,也即等待它之前所持有的object’s monitor被释放,通过notify()方法可以让该线程重新处于活动状态,从而去抢夺object’s monitor,唤醒该线程。
(2)如果多个线程同时处于等待状态,那么调用notify()方法只能随机唤醒一个线程。
(3)在同一时间内,只有一个线程能够获得object’s monitor,执行完毕之后,则再将其释放供其它线程抢占。
当然,如何使线程成为object‘s monitor的持有者,我会在多线程的其他博客中讲解。

接下来,我们再来看看notifyAll()方法:
  1. /** 
  2.      * Wakes up all threads that are waiting on this object’s monitor. A 
  3.      * thread waits on an object’s monitor by calling one of the 
  4.      * {@code wait} methods. 
  5.      * <p> 
  6.      * The awakened threads will not be able to proceed until the current 
  7.      * thread relinquishes the lock on this object. The awakened threads 
  8.      * will compete in the usual manner with any other threads that might 
  9.      * be actively competing to synchronize on this object; for example, 
  10.      * the awakened threads enjoy no reliable privilege or disadvantage in 
  11.      * being the next thread to lock this object. 
  12.      * <p> 
  13.      * This method should only be called by a thread that is the owner 
  14.      * of this object’s monitor. See the {@code notify} method for a 
  15.      * description of the ways in which a thread can become the owner of 
  16.      * a monitor. 
  17.      * 
  18.      * @exception  IllegalMonitorStateException  if the current thread is not 
  19.      *               the owner of this object’s monitor. 
  20.      * @see        java.lang.Object#notify() 
  21.      * @see        java.lang.Object#wait() 
  22.      */  
  23.     public final native void notifyAll();  
/**
     * Wakes up all threads that are waiting on this object's monitor. A
     * thread waits on an object's monitor by calling one of the
     * {@code wait} methods.
     * <p>
     * The awakened threads will not be able to proceed until the current
     * thread relinquishes the lock on this object. The awakened threads
     * will compete in the usual manner with any other threads that might
     * be actively competing to synchronize on this object; for example,
     * the awakened threads enjoy 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. 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 this object's monitor.
     * @see        java.lang.Object#notify()
     * @see        java.lang.Object#wait()
     */
    public final native void notifyAll();
那么顾名思义,notifyAll()就是用来唤醒正在等待状态中的所有线程的,不过也需要注意以下几点:
(1)notifyAll()只会唤醒那些等待抢占指定object’s monitor的线程,其他线程则不会被唤醒。
(2)notifyAll()只会一个一个的唤醒,而并非统一唤醒。因为在同一时间内,只有一个线程能够持有object’s monitor
(3)notifyAll()只是随机的唤醒线程,并非有序唤醒。
那么如何做到有序唤醒是我们接下来要讨论的问题。

notify()实现有序唤醒的思路和实现

就上节提出的问题,我们在这节中可以进行一下思考和讨论。
首先,简单来说,我们需要去解决的其实就是对于多线程针对object’s monitor的有序化。那么根据这一思路,我直接上代码:
  1. public class MyThreadFactory {  
  2.   
  3.     // 线程A是否处于等待状态的标志  
  4.     private boolean isThreadAWaiting;  
  5.     // 线程B是否处于等待状态的标志  
  6.     private boolean isThreadBWaiting;  
  7.     // 线程C是否处于等待状态的标志  
  8.     private boolean isThreadCWaiting;  
  9.   
  10.     public MyThreadFactory() {  
  11.         isThreadAWaiting = true;  
  12.         isThreadBWaiting = true;  
  13.         isThreadCWaiting = true;  
  14.     }  
  15.   
  16.     /** 
  17.      * 对象锁 
  18.      */  
  19.     private final Object object = new Object();  
  20.   
  21.     /** 
  22.      * 该线程作为一个唤醒线程 
  23.      */  
  24.     public void startWakenThread() {  
  25.         Thread t = new Thread(new Runnable() {  
  26.             @Override  
  27.             public void run() {  
  28.                 synchronized (object) {  
  29.                     System.out.println(”唤醒线程开始执行…”);  
  30.                     // 首先释放线程A  
  31.                     quitThreadA();  
  32.                 }  
  33.             }  
  34.         });  
  35.         t.start();  
  36.     }  
  37.   
  38.     /** 
  39.      * 启动线程A 
  40.      */  
  41.     public void startThreadA() {  
  42.         Thread t = new Thread(new Runnable() {  
  43.             @Override  
  44.             public void run() {  
  45.                 synchronized (object) {  
  46.                     System.out.println(”线程A开始等待…”);  
  47.                     try {  
  48.                         for (; ; ) {  
  49.                             if (!isThreadAWaiting) break;  
  50.                             object.wait();  
  51.                         }  
  52.                     } catch (InterruptedException e) {  
  53.                         e.printStackTrace();  
  54.                     }  
  55.                     System.out.println(”线程A结束…”);  
  56.                     // 线程A结束后,暂停2秒释放线程B  
  57.                     try {  
  58.                         Thread.sleep(2000);  
  59.                     } catch (InterruptedException e) {  
  60.                         e.printStackTrace();  
  61.                     }  
  62.                     quitThreadB();  
  63.                 }  
  64.             }  
  65.         });  
  66.         t.start();  
  67.     }  
  68.       
  69.     /** 
  70.      * 启动线程B 
  71.      */  
  72.     public void startThreadB() {  
  73.         Thread t = new Thread(new Runnable() {  
  74.             @Override  
  75.             public void run() {  
  76.                 synchronized (object) {  
  77.                     System.out.println(”线程B开始等待…”);  
  78.                     try {  
  79.                         for (; ; ) {  
  80.                             if (!isThreadBWaiting) break;  
  81.                             object.wait();  
  82.                         }  
  83.                     } catch (InterruptedException e) {  
  84.                         e.printStackTrace();  
  85.                     }  
  86.                     System.out.println(”线程B结束…”);  
  87.                     // 线程B结束后,暂停2秒释放线程C  
  88.                     try {  
  89.                         Thread.sleep(2000);  
  90.                     } catch (InterruptedException e) {  
  91.                         e.printStackTrace();  
  92.                     }  
  93.                     quitThreadC();  
  94.                 }  
  95.             }  
  96.         });  
  97.         t.start();  
  98.     }  
  99.   
  100.     /** 
  101.      * 启动线程C 
  102.      */  
  103.     public void startThreadC() {  
  104.         Thread t = new Thread(new Runnable() {  
  105.             @Override  
  106.             public void run() {  
  107.                 synchronized (object) {  
  108.                     System.out.println(”线程C开始等待…”);  
  109.                     try {  
  110.                         for (; ; ) {  
  111.                             if (!isThreadCWaiting) break;  
  112.                             object.wait();  
  113.                         }  
  114.                     } catch (InterruptedException e) {  
  115.                         e.printStackTrace();  
  116.                     }  
  117.                     System.out.println(”线程C结束…”);  
  118.   
  119.                     try {  
  120.                         Thread.sleep(1000);  
  121.                     } catch (InterruptedException e) {  
  122.                         e.printStackTrace();  
  123.                     }  
  124.                     System.out.println(”所有线程执行完毕!”);  
  125.                 }  
  126.             }  
  127.         });  
  128.         t.start();  
  129.     }  
  130.   
  131.     /** 
  132.      * 线程A退出等待 
  133.      */  
  134.     private void quitThreadA() {  
  135.         isThreadAWaiting = false;  
  136.         object.notify();  
  137.     }  
  138.       
  139.     /** 
  140.      * 线程B退出等待 
  141.      */  
  142.     private void quitThreadB() {  
  143.         isThreadBWaiting = false;  
  144.         object.notify();  
  145.     }  
  146.       
  147.     /** 
  148.      * 线程C退出等待 
  149.      */  
  150.     private void quitThreadC() {  
  151.         isThreadCWaiting = false;  
  152.         object.notify();  
  153.     }  
  154. }  
public class MyThreadFactory {

    // 线程A是否处于等待状态的标志
    private boolean isThreadAWaiting;
    // 线程B是否处于等待状态的标志
    private boolean isThreadBWaiting;
    // 线程C是否处于等待状态的标志
    private boolean isThreadCWaiting;

    public MyThreadFactory() {
        isThreadAWaiting = true;
        isThreadBWaiting = true;
        isThreadCWaiting = true;
    }

    /**
     * 对象锁
     */
    private final Object object = new Object();

    /**
     * 该线程作为一个唤醒线程
     */
    public void startWakenThread() {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("唤醒线程开始执行...");
                    // 首先释放线程A
                    quitThreadA();
                }
            }
        });
        t.start();
    }

    /**
     * 启动线程A
     */
    public void startThreadA() {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("线程A开始等待...");
                    try {
                        for (; ; ) {
                            if (!isThreadAWaiting) break;
                            object.wait();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程A结束...");
                    // 线程A结束后,暂停2秒释放线程B
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    quitThreadB();
                }
            }
        });
        t.start();
    }

    /**
     * 启动线程B
     */
    public void startThreadB() {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("线程B开始等待...");
                    try {
                        for (; ; ) {
                            if (!isThreadBWaiting) break;
                            object.wait();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程B结束...");
                    // 线程B结束后,暂停2秒释放线程C
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    quitThreadC();
                }
            }
        });
        t.start();
    }

    /**
     * 启动线程C
     */
    public void startThreadC() {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("线程C开始等待...");
                    try {
                        for (; ; ) {
                            if (!isThreadCWaiting) break;
                            object.wait();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程C结束...");

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("所有线程执行完毕!");
                }
            }
        });
        t.start();
    }

    /**
     * 线程A退出等待
     */
    private void quitThreadA() {
        isThreadAWaiting = false;
        object.notify();
    }

    /**
     * 线程B退出等待
     */
    private void quitThreadB() {
        isThreadBWaiting = false;
        object.notify();
    }

    /**
     * 线程C退出等待
     */
    private void quitThreadC() {
        isThreadCWaiting = false;
        object.notify();
    }
}

在以上代码中,我写了三个线程A,B,C用来作为等待线程,并且最后通过一个唤醒线程来唤醒这三个线程。
我的思路是这样的:
(1)通过notify()方法可以保证每次只唤醒一个线程,但是不能确保唤醒的是哪个线程。
(2)在线程A,B,C中,添加for或者while循环的方式使其进入无限等待的状态。这样能够保证notify()无论如何都不能唤醒线程。
(3)分别给A,B,C线程设置各自的标记,如果要唤醒该线程的话,就改变其状态并且跳出死循环,在最后执行下一个线程。

那么最终调用的main函数如下:
  1. public static void main(String[] args) {  
  2.         MyThreadFactory factory = new MyThreadFactory();  
  3.         factory.startThreadA();  
  4.         factory.startThreadB();  
  5.         factory.startThreadC();  
  6.   
  7.         try {  
  8.             Thread.sleep(3000);  
  9.         } catch (InterruptedException e) {  
  10.             e.printStackTrace();  
  11.         }  
  12.         factory.startWakenThread();  
  13.     }  
public static void main(String[] args) {
        MyThreadFactory factory = new MyThreadFactory();
        factory.startThreadA();
        factory.startThreadB();
        factory.startThreadC();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        factory.startWakenThread();
    }

最后运行的结果如下:


小结

本章是为了通过wait(),sleep(),notify()以及notifyAll()去了解它们是如何使用的,以及它们各自的意义。现在在java.util.concurrent包中,已经有很多针对多线程并发的线程池封装类和接口,大家使用起来会更加灵活和方便,并且能够实现更多并发效果。因此不太推荐大家使用wait()和notify()这种方式。在接下来的博客中,我还会继续深入多线程的知识,给大家带来更多更深入的东西。希望大家给予支持。

转载这篇文章,更加深入的了解了多线程相互通信的知识,十分有帮助.但是上面ABC的例子我有一些疑惑:
notify()不是应该随机唤醒等待中的单个线程吗?但是上面的例子就是哪个线程先开启就唤醒了哪个.
我的分析是:如果notify()方法是随机唤醒等待的单个线程,那么假设主方法中factory.startWakenThread()执行后,随机唤醒的是线程C,由于factory.startWakenThread()方法中调用的quitThreadA(); 只改变了线程A的标记isThreadAWaiting,并没有改变线程C的标记isThreadCWaiting,所以这时线程C被唤醒后继续执行for循环,遇见wait()方法继续等待,这样三个线程又处于全部等待状态,不会出现上面ABC顺序输出的结果.
而既然出现了上面的顺序唤醒结果,那么是否有悖于notify()方法是随机唤醒等待线程这个结论呢?暂时我还不太明白,是不是自己思考的有漏洞,暂时记下,努力查证中….

大家在学习Java的过程中,势必要进行多线程的系统学习,这部分内容知识对于你在工作中的影响是极大的,并且在面试的过程中,这部分知识也是必然会被问到的。既然多线程的知识如此重要,那么我们就不能浅尝辄止。在这篇文章中,我想通过阅读源码的方式给大家分享一下我自己对于Java中wait(),notify()和sleep()的理解,并且在后面我也做一些内容上的扩展,与大家共同思考。如果在文章中有描述不妥或者逻辑错误的地方,欢迎大家指正,谢谢!

wait()和sleep()

我们先简单的了解一下wait()和sleep()这两个方法:

首先wait()是属于Object类的方法,从源码给出的解释来看,wait()方法可以做到如下几点:

(1)首先,调用了wait()之后会引起当前线程处于等待状状态。

(2)其次,每个线程必须持有该对象的monitor。如果在当前线程中调用wait()方法之后,该线程就会释放monitor的持有对象并让自己处于等待状态。

(3)如果想唤醒一个正在等待的线程,那么需要开启一个线程通过notify()或者notifyAll()方法去通知正在等待的线程获取monitor对象。如此,该线程即可打破等待的状态继续执行代码。

  1. /** 
  2.      * Causes the current thread to wait until another thread invokes the 
  3.      * {@link java.lang.Object#notify()} method or the 
  4.      * {@link java.lang.Object#notifyAll()} method for this object. 
  5.      * In other words, this method behaves exactly as if it simply 
  6.      * performs the call {@code wait(0)}. 
  7.      * <p> 
  8.      * The current thread must own this object’s monitor. The thread 
  9.      * releases ownership of this monitor and waits until another thread 
  10.      * notifies threads waiting on this object’s monitor to wake up 
  11.      * either through a call to the {@code notify} method or the 
  12.      * {@code notifyAll} method. The thread then waits until it can 
  13.      * re-obtain ownership of the monitor and resumes execution. 
  14.      * <p> 
  15.      * As in the one argument version, interrupts and spurious wakeups are 
  16.      * possible, and this method should always be used in a loop: 
  17.      * <pre> 
  18.      *     synchronized (obj) { 
  19.      *         while (<condition does not hold>) 
  20.      *             obj.wait(); 
  21.      *         … // Perform action appropriate to condition 
  22.      *     } 
  23.      * </pre> 
  24.      * This method should only be called by a thread that is the owner 
  25.      * of this object’s monitor. See the {@code notify} method for a 
  26.      * description of the ways in which a thread can become the owner of 
  27.      * a monitor. 
  28.      * 
  29.      * @exception  IllegalMonitorStateException  if the current thread is not 
  30.      *               the owner of the object’s monitor. 
  31.      * @exception  InterruptedException if any thread interrupted the 
  32.      *             current thread before or while the current thread 
  33.      *             was waiting for a notification.  The <i>interrupted 
  34.      *             status</i> of the current thread is cleared when 
  35.      *             this exception is thrown. 
  36.      * @see        java.lang.Object#notify() 
  37.      * @see        java.lang.Object#notifyAll() 
  38.      */  
  39.     public final void wait() throws InterruptedException {  
  40.         wait(0);  
  41.     }  
/**
     * 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 (<condition does not hold>)
     *             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);
    }

代码演示:

  1. public class Main {  
  2.     public static void main(String[] args) {  
  3.         Main main = new Main();  
  4.         main.startThread();  
  5.     }  
  6.   
  7.     /** 
  8.      * 线程锁 
  9.      */  
  10.     private final Object object = new Object();  
  11.   
  12.     /** 
  13.      * 启动线程 
  14.      */  
  15.     public void startThread() {  
  16.         Thread t = new Thread(new Runnable() {  
  17.             @Override  
  18.             public void run() {  
  19.                 System.out.println(”开始执行线程。。。”);  
  20.                 System.out.println(”进入等待状态。。。”);  
  21.                 synchronized (object) {  
  22.                     try {  
  23.                         object.wait();  
  24.                     } catch (InterruptedException e) {  
  25.                         e.printStackTrace();  
  26.                     }  
  27.                 }  
  28.                 System.out.println(”线程结束。。。”);  
  29.             }  
  30.         });  
  31.         t.start();  
  32.     }  
  33. }  
public class Main {
    public static void main(String[] args) {
        Main main = new Main();
        main.startThread();
    }

    /**
     * 线程锁
     */
    private final Object object = new Object();

    /**
     * 启动线程
     */
    public void startThread() {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("开始执行线程。。。");
                System.out.println("进入等待状态。。。");
                synchronized (object) {
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("线程结束。。。");
            }
        });
        t.start();
    }
}

从代码来看,在执行线程和线程结束之间,我们先让该线程获取object对象作为自己的object’s monitor,然后调用了object对象的wait()方法从而让其进入等待状态。那么程序运行的结果如下:


程序在未被唤醒之后,将不再打印“线程结束”,并且程序无法执行完毕一直处于等待状态。


sleep()方法来自于Thread类,从源码给出的解释来看,sleep()方法可以做到如下几点:

(1)首先,调用sleep()之后,会引起当前执行的线程进入暂时中断状态,也即睡眠状态。

(2)其次,虽然当前线程进入了睡眠状态,但是依然持有monitor对象。

(3)在中断完成之后,自动进入唤醒状态从而继续执行代码。

  1. /** 
  2.     * Causes the currently executing thread to sleep (temporarily cease 
  3.     * execution) for the specified number of milliseconds, subject to 
  4.     * the precision and accuracy of system timers and schedulers. The thread 
  5.     * does not lose ownership of any monitors. 
  6.     * 
  7.     * @param  millis 
  8.     *         the length of time to sleep in milliseconds 
  9.     * 
  10.     * @throws  IllegalArgumentException 
  11.     *          if the value of {@code millis} is negative 
  12.     * 
  13.     * @throws  InterruptedException 
  14.     *          if any thread has interrupted the current thread. The 
  15.     *          <i>interrupted status</i> of the current thread is 
  16.     *          cleared when this exception is thrown. 
  17.     */  
  18.    public static native void sleep(long millis) throws InterruptedException;  
 /**
     * 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;


代码演示:

  1. public class Main {  
  2.     public static void main(String[] args) {  
  3.         Main main = new Main();  
  4.         main.startThread();  
  5.     }  
  6.   
  7.     /** 
  8.      * 启动线程 
  9.      */  
  10.     public void startThread() {  
  11.         Thread t = new Thread(new Runnable() {  
  12.             @Override  
  13.             public void run() {  
  14.                 System.out.println(”开始执行线程。。。”);  
  15.                 System.out.println(”进入睡眠状态。。。”);  
  16.                 try {  
  17.                     Thread.sleep(3000);  
  18.                 } catch (InterruptedException e) {  
  19.                     e.printStackTrace();  
  20.                 }  
  21.                 System.out.println(”线程结束。。。”);  
  22.             }  
  23.         });  
  24.         t.start();  
  25.     }  
  26. }  
public class Main {
    public static void main(String[] args) {
        Main main = new Main();
        main.startThread();
    }

    /**
     * 启动线程
     */
    public void startThread() {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("开始执行线程。。。");
                System.out.println("进入睡眠状态。。。");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程结束。。。");
            }
        });
        t.start();
    }
}

从运行的结果来看,我们可以看出程序虽然在运行过程中中断了3秒,但是在3秒结束之后依然会继续执行代码,直到运行结束。在睡眠的期间内,线程会一直持有monitor对象。



那么从以上的理论和实践来分析,我们能得出如下结论:

(1)在线程的运行过程中,调用该线程持有monitor对象的wait()方法时,该线程首先会进入等待状态,并将自己持有的monitor对象释放。

(2)如果一个线程正处于等待状态时,那么唤醒它的办法就是开启一个新的线程,通过notify()或者notifyAll()的方式去唤醒。当然,需要注意的一点就是,必须是同一个monitor对象。

(3)sleep()方法虽然会使线程中断,但是不会将自己的monitor对象释放,在中断结束后,依然能够保持代码继续执行。


notify()和notifyAll()

说完了wait()方法之后,我们接下来讨论一下Object类中的另外两个与wait()相关的方法。首先还是通过源码的方式让大家先初步了解一下:
  1. /** 
  2.      * Wakes up a single thread that is waiting on this object’s 
  3.      * monitor. If any threads are waiting on this object, one of them 
  4.      * is chosen to be awakened. The choice is arbitrary and occurs at 
  5.      * the discretion of the implementation. A thread waits on an object’s 
  6.      * monitor by calling one of the {@code wait} methods. 
  7.      * <p> 
  8.      * The awakened thread will not be able to proceed until the current 
  9.      * thread relinquishes the lock on this object. The awakened thread will 
  10.      * compete in the usual manner with any other threads that might be 
  11.      * actively competing to synchronize on this object; for example, the 
  12.      * awakened thread enjoys no reliable privilege or disadvantage in being 
  13.      * the next thread to lock this object. 
  14.      * <p> 
  15.      * This method should only be called by a thread that is the owner 
  16.      * of this object’s monitor. A thread becomes the owner of the 
  17.      * object’s monitor in one of three ways: 
  18.      * <ul> 
  19.      * <li>By executing a synchronized instance method of that object. 
  20.      * <li>By executing the body of a {@code synchronized} statement 
  21.      *     that synchronizes on the object. 
  22.      * <li>For objects of type {@code Class,} by executing a 
  23.      *     synchronized static method of that class. 
  24.      * </ul> 
  25.      * <p> 
  26.      * Only one thread at a time can own an object’s monitor. 
  27.      * 
  28.      * @exception  IllegalMonitorStateException  if the current thread is not 
  29.      *               the owner of this object’s monitor. 
  30.      * @see        java.lang.Object#notifyAll() 
  31.      * @see        java.lang.Object#wait() 
  32.      */  
  33.     public final native void notify();  
/**
     * 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();
先来看下notify()这个方法,通过阅读源码我们可以总结一下几点:
(1)当一个线程处于wait()状态时,也即等待它之前所持有的object’s monitor被释放,通过notify()方法可以让该线程重新处于活动状态,从而去抢夺object’s monitor,唤醒该线程。
(2)如果多个线程同时处于等待状态,那么调用notify()方法只能随机唤醒一个线程。
(3)在同一时间内,只有一个线程能够获得object’s monitor,执行完毕之后,则再将其释放供其它线程抢占。
当然,如何使线程成为object‘s monitor的持有者,我会在多线程的其他博客中讲解。

接下来,我们再来看看notifyAll()方法:
  1. /** 
  2.      * Wakes up all threads that are waiting on this object’s monitor. A 
  3.      * thread waits on an object’s monitor by calling one of the 
  4.      * {@code wait} methods. 
  5.      * <p> 
  6.      * The awakened threads will not be able to proceed until the current 
  7.      * thread relinquishes the lock on this object. The awakened threads 
  8.      * will compete in the usual manner with any other threads that might 
  9.      * be actively competing to synchronize on this object; for example, 
  10.      * the awakened threads enjoy no reliable privilege or disadvantage in 
  11.      * being the next thread to lock this object. 
  12.      * <p> 
  13.      * This method should only be called by a thread that is the owner 
  14.      * of this object’s monitor. See the {@code notify} method for a 
  15.      * description of the ways in which a thread can become the owner of 
  16.      * a monitor. 
  17.      * 
  18.      * @exception  IllegalMonitorStateException  if the current thread is not 
  19.      *               the owner of this object’s monitor. 
  20.      * @see        java.lang.Object#notify() 
  21.      * @see        java.lang.Object#wait() 
  22.      */  
  23.     public final native void notifyAll();  
/**
     * Wakes up all threads that are waiting on this object's monitor. A
     * thread waits on an object's monitor by calling one of the
     * {@code wait} methods.
     * <p>
     * The awakened threads will not be able to proceed until the current
     * thread relinquishes the lock on this object. The awakened threads
     * will compete in the usual manner with any other threads that might
     * be actively competing to synchronize on this object; for example,
     * the awakened threads enjoy 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. 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 this object's monitor.
     * @see        java.lang.Object#notify()
     * @see        java.lang.Object#wait()
     */
    public final native void notifyAll();
那么顾名思义,notifyAll()就是用来唤醒正在等待状态中的所有线程的,不过也需要注意以下几点:
(1)notifyAll()只会唤醒那些等待抢占指定object’s monitor的线程,其他线程则不会被唤醒。
(2)notifyAll()只会一个一个的唤醒,而并非统一唤醒。因为在同一时间内,只有一个线程能够持有object’s monitor
(3)notifyAll()只是随机的唤醒线程,并非有序唤醒。
那么如何做到有序唤醒是我们接下来要讨论的问题。

notify()实现有序唤醒的思路和实现

就上节提出的问题,我们在这节中可以进行一下思考和讨论。
首先,简单来说,我们需要去解决的其实就是对于多线程针对object’s monitor的有序化。那么根据这一思路,我直接上代码:
  1. public class MyThreadFactory {  
  2.   
  3.     // 线程A是否处于等待状态的标志  
  4.     private boolean isThreadAWaiting;  
  5.     // 线程B是否处于等待状态的标志  
  6.     private boolean isThreadBWaiting;  
  7.     // 线程C是否处于等待状态的标志  
  8.     private boolean isThreadCWaiting;  
  9.   
  10.     public MyThreadFactory() {  
  11.         isThreadAWaiting = true;  
  12.         isThreadBWaiting = true;  
  13.         isThreadCWaiting = true;  
  14.     }  
  15.   
  16.     /** 
  17.      * 对象锁 
  18.      */  
  19.     private final Object object = new Object();  
  20.   
  21.     /** 
  22.      * 该线程作为一个唤醒线程 
  23.      */  
  24.     public void startWakenThread() {  
  25.         Thread t = new Thread(new Runnable() {  
  26.             @Override  
  27.             public void run() {  
  28.                 synchronized (object) {  
  29.                     System.out.println(”唤醒线程开始执行…”);  
  30.                     // 首先释放线程A  
  31.                     quitThreadA();  
  32.                 }  
  33.             }  
  34.         });  
  35.         t.start();  
  36.     }  
  37.   
  38.     /** 
  39.      * 启动线程A 
  40.      */  
  41.     public void startThreadA() {  
  42.         Thread t = new Thread(new Runnable() {  
  43.             @Override  
  44.             public void run() {  
  45.                 synchronized (object) {  
  46.                     System.out.println(”线程A开始等待…”);  
  47.                     try {  
  48.                         for (; ; ) {  
  49.                             if (!isThreadAWaiting) break;  
  50.                             object.wait();  
  51.                         }  
  52.                     } catch (InterruptedException e) {  
  53.                         e.printStackTrace();  
  54.                     }  
  55.                     System.out.println(”线程A结束…”);  
  56.                     // 线程A结束后,暂停2秒释放线程B  
  57.                     try {  
  58.                         Thread.sleep(2000);  
  59.                     } catch (InterruptedException e) {  
  60.                         e.printStackTrace();  
  61.                     }  
  62.                     quitThreadB();  
  63.                 }  
  64.             }  
  65.         });  
  66.         t.start();  
  67.     }  
  68.       
  69.     /** 
  70.      * 启动线程B 
  71.      */  
  72.     public void startThreadB() {  
  73.         Thread t = new Thread(new Runnable() {  
  74.             @Override  
  75.             public void run() {  
  76.                 synchronized (object) {  
  77.                     System.out.println(”线程B开始等待…”);  
  78.                     try {  
  79.                         for (; ; ) {  
  80.                             if (!isThreadBWaiting) break;  
  81.                             object.wait();  
  82.                         }  
  83.                     } catch (InterruptedException e) {  
  84.                         e.printStackTrace();  
  85.                     }  
  86.                     System.out.println(”线程B结束…”);  
  87.                     // 线程B结束后,暂停2秒释放线程C  
  88.                     try {  
  89.                         Thread.sleep(2000);  
  90.                     } catch (InterruptedException e) {  
  91.                         e.printStackTrace();  
  92.                     }  
  93.                     quitThreadC();  
  94.                 }  
  95.             }  
  96.         });  
  97.         t.start();  
  98.     }  
  99.   
  100.     /** 
  101.      * 启动线程C 
  102.      */  
  103.     public void startThreadC() {  
  104.         Thread t = new Thread(new Runnable() {  
  105.             @Override  
  106.             public void run() {  
  107.                 synchronized (object) {  
  108.                     System.out.println(”线程C开始等待…”);  
  109.                     try {  
  110.                         for (; ; ) {  
  111.                             if (!isThreadCWaiting) break;  
  112.                             object.wait();  
  113.                         }  
  114.                     } catch (InterruptedException e) {  
  115.                         e.printStackTrace();  
  116.                     }  
  117.                     System.out.println(”线程C结束…”);  
  118.   
  119.                     try {  
  120.                         Thread.sleep(1000);  
  121.                     } catch (InterruptedException e) {  
  122.                         e.printStackTrace();  
  123.                     }  
  124.                     System.out.println(”所有线程执行完毕!”);  
  125.                 }  
  126.             }  
  127.         });  
  128.         t.start();  
  129.     }  
  130.   
  131.     /** 
  132.      * 线程A退出等待 
  133.      */  
  134.     private void quitThreadA() {  
  135.         isThreadAWaiting = false;  
  136.         object.notify();  
  137.     }  
  138.       
  139.     /** 
  140.      * 线程B退出等待 
  141.      */  
  142.     private void quitThreadB() {  
  143.         isThreadBWaiting = false;  
  144.         object.notify();  
  145.     }  
  146.       
  147.     /** 
  148.      * 线程C退出等待 
  149.      */  
  150.     private void quitThreadC() {  
  151.         isThreadCWaiting = false;  
  152.         object.notify();  
  153.     }  
  154. }  
public class MyThreadFactory {

    // 线程A是否处于等待状态的标志
    private boolean isThreadAWaiting;
    // 线程B是否处于等待状态的标志
    private boolean isThreadBWaiting;
    // 线程C是否处于等待状态的标志
    private boolean isThreadCWaiting;

    public MyThreadFactory() {
        isThreadAWaiting = true;
        isThreadBWaiting = true;
        isThreadCWaiting = true;
    }

    /**
     * 对象锁
     */
    private final Object object = new Object();

    /**
     * 该线程作为一个唤醒线程
     */
    public void startWakenThread() {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("唤醒线程开始执行...");
                    // 首先释放线程A
                    quitThreadA();
                }
            }
        });
        t.start();
    }

    /**
     * 启动线程A
     */
    public void startThreadA() {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("线程A开始等待...");
                    try {
                        for (; ; ) {
                            if (!isThreadAWaiting) break;
                            object.wait();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程A结束...");
                    // 线程A结束后,暂停2秒释放线程B
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    quitThreadB();
                }
            }
        });
        t.start();
    }

    /**
     * 启动线程B
     */
    public void startThreadB() {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("线程B开始等待...");
                    try {
                        for (; ; ) {
                            if (!isThreadBWaiting) break;
                            object.wait();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程B结束...");
                    // 线程B结束后,暂停2秒释放线程C
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    quitThreadC();
                }
            }
        });
        t.start();
    }

    /**
     * 启动线程C
     */
    public void startThreadC() {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("线程C开始等待...");
                    try {
                        for (; ; ) {
                            if (!isThreadCWaiting) break;
                            object.wait();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程C结束...");

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("所有线程执行完毕!");
                }
            }
        });
        t.start();
    }

    /**
     * 线程A退出等待
     */
    private void quitThreadA() {
        isThreadAWaiting = false;
        object.notify();
    }

    /**
     * 线程B退出等待
     */
    private void quitThreadB() {
        isThreadBWaiting = false;
        object.notify();
    }

    /**
     * 线程C退出等待
     */
    private void quitThreadC() {
        isThreadCWaiting = false;
        object.notify();
    }
}

在以上代码中,我写了三个线程A,B,C用来作为等待线程,并且最后通过一个唤醒线程来唤醒这三个线程。
我的思路是这样的:
(1)通过notify()方法可以保证每次只唤醒一个线程,但是不能确保唤醒的是哪个线程。
(2)在线程A,B,C中,添加for或者while循环的方式使其进入无限等待的状态。这样能够保证notify()无论如何都不能唤醒线程。
(3)分别给A,B,C线程设置各自的标记,如果要唤醒该线程的话,就改变其状态并且跳出死循环,在最后执行下一个线程。

那么最终调用的main函数如下:
  1. public static void main(String[] args) {  
  2.         MyThreadFactory factory = new MyThreadFactory();  
  3.         factory.startThreadA();  
  4.         factory.startThreadB();  
  5.         factory.startThreadC();  
  6.   
  7.         try {  
  8.             Thread.sleep(3000);  
  9.         } catch (InterruptedException e) {  
  10.             e.printStackTrace();  
  11.         }  
  12.         factory.startWakenThread();  
  13.     }  
public static void main(String[] args) {
        MyThreadFactory factory = new MyThreadFactory();
        factory.startThreadA();
        factory.startThreadB();
        factory.startThreadC();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        factory.startWakenThread();
    }

最后运行的结果如下:


小结

本章是为了通过wait(),sleep(),notify()以及notifyAll()去了解它们是如何使用的,以及它们各自的意义。现在在java.util.concurrent包中,已经有很多针对多线程并发的线程池封装类和接口,大家使用起来会更加灵活和方便,并且能够实现更多并发效果。因此不太推荐大家使用wait()和notify()这种方式。在接下来的博客中,我还会继续深入多线程的知识,给大家带来更多更深入的东西。希望大家给予支持。

转载这篇文章,更加深入的了解了多线程相互通信的知识,十分有帮助.但是上面ABC的例子我有一些疑惑:
notify()不是应该随机唤醒等待中的单个线程吗?但是上面的例子就是哪个线程先开启就唤醒了哪个.
我的分析是:如果notify()方法是随机唤醒等待的单个线程,那么假设主方法中factory.startWakenThread()执行后,随机唤醒的是线程C,由于factory.startWakenThread()方法中调用的quitThreadA(); 只改变了线程A的标记isThreadAWaiting,并没有改变线程C的标记isThreadCWaiting,所以这时线程C被唤醒后继续执行for循环,遇见wait()方法继续等待,这样三个线程又处于全部等待状态,不会出现上面ABC顺序输出的结果.
而既然出现了上面的顺序唤醒结果,那么是否有悖于notify()方法是随机唤醒等待线程这个结论呢?暂时我还不太明白,是不是自己思考的有漏洞,暂时记下,努力查证中….

猜你喜欢

转载自blog.csdn.net/xiaodunlp/article/details/80650665
今日推荐