java-wait-and-notify使用


概述

java中多线程常用到wait和notify等方法,但是对此缺乏一个清晰的认识,本文主要介绍JAVA中wait和notify以及notifyAll的使用.

Wait

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

wait方法调用后会使持有该对象的线程把对象的控制权交出去,也就是释放锁,然后原来的线程处于等待状态.
wait和notify是用来做线程通信的,wait调用后必须是另一个线程中调用notify或者notifyAll来唤醒线程.
并且wait方法只能在拥有前对象的镜像中调用,也就是在synchronized同步代码块中调用,否则会报IllegalMonitorStateException异常.

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.
         *
         * @throws  IllegalMonitorStateException  if the current thread is not
         *               the owner of this object's monitor.
         * @see        java.lang.Object#notifyAll()
         * @see        java.lang.Object#wait()
         */

通知某个正在等待这个对象的控制权的线程可以继续运行,注意这里唤醒的是一个单一线程使该线程重新获取这个对象的锁,获取锁并不代表就可以马上执行.
新唤醒的线程不会马上执行,而是和其他线程竞争执行权限,除非该线程设置了特有的线程优先级.
同样的必须在synchronized同步代码块中调用,否则会报IllegalMonitorStateException异常.

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.
         *
         * @throws  IllegalMonitorStateException  if the current thread is not
         *               the owner of this object's monitor.
         * @see        java.lang.Object#notify()
         * @see        java.lang.Object#wait()
         */

唤醒等待获取此对象控制权限的所有线程,同样的这些线程不会马上执行,而是去CPU哪里竞争执行权限.
也必须在synchronized同步代码块中执行,,否则会报IllegalMonitorStateException异常.

使用-例子-1

    package com.sefon.gateway;

    /**
    * @Description:    
    * @author: zhouqiang
    * @version: 1.0, Apr 4, 2019
    */

    public class OutputThread implements Runnable {

        private int num;
        private Object lock;

        public OutputThread(int num, Object lock) {
            super();
            this.num = num;
            this.lock = lock;
        }

        public void run() {
            try {
                while(true){
                    synchronized(lock){
                          lock.notifyAll();//如果此处先wait,在notify不会打印任何结果
                        lock.wait();
                        Thread.sleep(1000);
                        System.out.println(num);
                    }
                }
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }

        public static void main(String[] args){
            final Object lock = new Object();

            Thread thread1 = new Thread(new OutputThread(1, lock));
            Thread thread2 = new Thread(new OutputThread(2, lock));

            thread1.start();
            thread2.start();
        }

    }

这是一个轮询打印数字的多线程演示,为何lock先wait再notifyAll不会打印任何东西,而必须先notify才行呢?

分析:如果先wait,那线程1 2先后启动,然后都wait了那这样线程1 2都会等待另一个线程来notify。
但是显然这个线程3是不存在的,这样就没人来唤醒这两个等待中的线程,所以wait之后的代码便不会再执行了.

使用-例子-2

再来看下面一个简单的例子

WaitThread只负责wait然后执行业务代码

    package com.sefon.gateway;

    /**
    * @Description:    
    * @author: jackromer
    * @version: 1.0, Apr 4, 2019
    */

    public class WaitThread implements Runnable {


        private int num;
        private Object lock;

        public WaitThread(int num, Object lock) {
            super();
            this.num = num;
            this.lock = lock;
        }

        public void run() {
            try {
                synchronized(lock){
                    System.out.println(Thread.currentThread().getName() + " :wait for notify");
                    lock.wait();//wait调用后, wait之后的代码就暂时不会再执行了, 必须等到其他线程notifyAll, 直到当前线程重新在CPU哪里竞争到了object的锁,当前线程的后序代码才会执行 .
                    System.out.println(Thread.currentThread().getName() + " :i have bean waked up and i get the lock from cpu, i am free ,i will continue to process..., my num is :" + num);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

        public static void main(String[] args) throws InterruptedException {
            final Object lock = new Object();
            Thread waitThread1     = new Thread(new WaitThread(1, lock));
            Thread waitThread2     = new Thread(new WaitThread(2, lock));
            Thread waitThread3     = new Thread(new WaitThread(3, lock));
            Thread notifyThread = new Thread(new NotifyThread(lock));//notifyThread 用来唤醒之前wait的所有线程

            waitThread1.start();
            waitThread2.start();
            waitThread3.start();
            Thread.sleep(2000);//此处sleep是为了让wait的三个线程先启动,后面再启动notify线程。
            notifyThread.start();
        }

    }

NotifyThread只负责唤醒wait object的所有线程

    package com.sefon.gateway;

    /**
    * @Description:    
    * @author: jackromer
    * @version: 1.0, Apr 4, 2019
    */

    public class NotifyThread implements Runnable {

        private Object lock;
        public NotifyThread(Object lock) {
            super();
            this.lock = lock;
        }

        public void run() {//用于notify 所有等待此对象的线程
            synchronized(lock){
                lock.notifyAll();//注意,此处notifyAll虽然唤醒了所有等待object的线程,但是不一定按之前线程的先后顺序继续执行,而是谁先去CPU哪里竞争到锁谁就先执行
                System.out.println("I am notify thread ,i will wake up all threads which are waiting for this object.");
            }
        }

    }

启动main方法运行后的结果:

    //分析:线程执行顺序分别为0 2 1,这三个线程都会等待notify
    Thread-0 :wait for notify 
    Thread-2 :wait for notify
    Thread-1 :wait for notify
    //notifyAll线程唤醒了线程1 2 3,1 2 3线程重新去CPU哪里获取执行权限,明显的他们抢到执行权限的顺序为2 3 1
    I am notify thread ,i will wake up all threads which are waiting for this object.
    Thread-1 :i have bean waked up and i get the lock from cpu, i am free ,i will continue to process..., my num is :2
    Thread-2 :i have bean waked up and i get the lock from cpu, i am free ,i will continue to process..., my num is :3
    Thread-0 :i have bean waked up and i get the lock from cpu, i am free ,i will continue to process..., my num is :1

线程的生命周期

总结

wait notify和notifyAll如果光看注释,可能不那么容易理解,最关键的还是试验一下,会有不一样的收获,但只要记住一点这三个是用来做线程通信的,必须在同步代码块中执行,他们都是object顶层对象的方法,所以你应该清楚这三个和线程没关系而是和对象的锁有关系,而线程只是用到了锁而已关系结构可以理解为这样:
OBJECT(只有一个) — LOCK(只有一个) — THREADS(线程可以有很多个,但都必须用同一个对象的同一个锁)。

一盏灯, 一片昏黄; 一简书, 一杯淡茶。 守着那一份淡定, 品读属于自己的寂寞。 保持淡定, 才能欣赏到最美丽的风景! 保持淡定, 人生从此不再寂寞。



   Reprint policy


《java-wait-and-notify使用》 by jackromer is licensed under a Creative Commons Attribution 4.0 International License
 Previous
java锁分类和说明 java锁分类和说明
概述 本文主要介绍JAVA的锁分类和简单使用 锁分类 其实对于 Java 锁的分类没有严格意义的规则,我们常说的分类一般都是依据锁的特性、锁的设计、锁的状态等进行归纳整理的,大概分为以下几种。 公平锁,非公平锁 可重入锁 互
2019-08-27
Next 
spring-cloud简介和核心组件简介 spring-cloud简介和核心组件简介
概述 本文主要介绍 spring cloud 以及其相关的核心组件简介,对 spring cloud 有一个比较简明清晰的认识。 组件简介spring cloud Eureka Spring Cloud Eureka 是 Spring C
2019-08-27
  目录