大家在学习Java的过程中,势必要进行多线程的系统学习,这部分内容知识对于你在工作中的影响是极大的,并且在面试的过程中,这部分知识也是必然会被问到的。既然多线程的知识如此重要,那么我们就不能浅尝辄止。在这篇文章中,我想通过阅读源码的方式给大家分享一下我自己对于Java中wait(),notify()和sleep()的理解,并且在后面我也做一些内容上的扩展,与大家共同思考。如果在文章中有描述不妥或者逻辑错误的地方,欢迎大家指正,谢谢!
wait()和sleep()
我们先简单的了解一下wait()和sleep()这两个方法:
首先wait()是属于Object类的方法,从源码给出的解释来看,wait()方法可以做到如下几点:
(1)首先,调用了wait()之后会引起当前线程处于等待状状态。
(2)其次,每个线程必须持有该对象的monitor。如果在当前线程中调用wait()方法之后,该线程就会释放monitor的持有对象并让自己处于等待状态。
(3)如果想唤醒一个正在等待的线程,那么需要开启一个线程通过notify()或者notifyAll()方法去通知正在等待的线程获取monitor对象。如此,该线程即可打破等待的状态继续执行代码。
- /**
- * 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);
- }
/** * 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); }
代码演示:
- 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();
- }
- }
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)在中断完成之后,自动进入唤醒状态从而继续执行代码。
- /**
- * 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;
/** * 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;
代码演示:
- 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();
- }
- }
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()
- /**
- * 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();
/** * 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()这个方法,通过阅读源码我们可以总结一下几点:
- /**
- * 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();
/** * 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()就是用来唤醒正在等待状态中的所有线程的,不过也需要注意以下几点:
notify()实现有序唤醒的思路和实现
- 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();
- }
- }
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用来作为等待线程,并且最后通过一个唤醒线程来唤醒这三个线程。
- 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();
- }
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(); }
最后运行的结果如下:
小结
转载这篇文章,更加深入的了解了多线程相互通信的知识,十分有帮助.但是上面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对象。如此,该线程即可打破等待的状态继续执行代码。
- /**
- * 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);
- }
/** * 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); }
代码演示:
- 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();
- }
- }
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)在中断完成之后,自动进入唤醒状态从而继续执行代码。
- /**
- * 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;
/** * 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;
代码演示:
- 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();
- }
- }
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()
- /**
- * 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();
/** * 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()这个方法,通过阅读源码我们可以总结一下几点:
- /**
- * 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();
/** * 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()就是用来唤醒正在等待状态中的所有线程的,不过也需要注意以下几点:
notify()实现有序唤醒的思路和实现
- 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();
- }
- }
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用来作为等待线程,并且最后通过一个唤醒线程来唤醒这三个线程。
- 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();
- }
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(); }
最后运行的结果如下:
小结
转载这篇文章,更加深入的了解了多线程相互通信的知识,十分有帮助.但是上面ABC的例子我有一些疑惑:
notify()不是应该随机唤醒等待中的单个线程吗?但是上面的例子就是哪个线程先开启就唤醒了哪个.
我的分析是:如果notify()方法是随机唤醒等待的单个线程,那么假设主方法中factory.startWakenThread()执行后,随机唤醒的是线程C,由于factory.startWakenThread()方法中调用的quitThreadA(); 只改变了线程A的标记isThreadAWaiting,并没有改变线程C的标记isThreadCWaiting,所以这时线程C被唤醒后继续执行for循环,遇见wait()方法继续等待,这样三个线程又处于全部等待状态,不会出现上面ABC顺序输出的结果.
而既然出现了上面的顺序唤醒结果,那么是否有悖于notify()方法是随机唤醒等待线程这个结论呢?暂时我还不太明白,是不是自己思考的有漏洞,暂时记下,努力查证中….