美文网首页
09给女朋友讲讲并发编程-wait()和notify()

09给女朋友讲讲并发编程-wait()和notify()

作者: XueFengDong | 来源:发表于2021-01-15 21:02 被阅读0次

    一、API介绍

    • obj.wait() 让进入object监视器的线程到waitSet中等待(RUNNBALE -> TIMED_WAITING)
    • obj.notify() 随机唤醒一个waitSet中阻塞的线程
    • obj.notifyAll() 唤醒在waitSet中所有阻塞的线程
      上面三个方法都是线程间协作的手段,都属于Object对象的方法。必须获得此对象的锁,才可以调用这几个方法。
        static Object obj = new Object();
        
        public static void main(String[] args){
    
            Thread t2 = new Thread(() -> {
    
                synchronized (obj){
                    log.info("{}","t1进入等待...");
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    log.info("t1被唤醒");
                }
    
            }, "t1");
    
            Thread t1 = new Thread(() -> {
    
                synchronized (obj){
                    log.info("{}","t2进入等待...");
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    log.info("t2被唤醒");
                }
    
            }, "t2");
    
            t1.start();
            t2.start();
    
            try {
                //主线程休眠2秒
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (obj){
                log.info("{}","主线程随机唤醒一个线程");
                obj.notify();
            }
            
        }
    

    输出结果:

    21:35:24.174  INFO [t1] com.dxf.线程的方法.Test9 - t1进入等待...
    21:35:24.175  INFO [t2] com.dxf.线程的方法.Test9 - t2进入等待...
    21:35:26.173  INFO [main] com.dxf.线程的方法.Test9 - 主线程随机唤醒一个线程
    21:35:26.173  INFO [t1] com.dxf.线程的方法.Test9 - t1被唤醒
    

    如果换成notifyAll():

    21:36:46.677  INFO [t2] com.dxf.线程的方法.Test9 - t2进入等待...
    21:36:46.678  INFO [t1] com.dxf.线程的方法.Test9 - t1进入等待...
    21:36:48.677  INFO [main] com.dxf.线程的方法.Test9 - 主线程唤醒全部waitSet中的线程
    21:36:48.677  INFO [t1] com.dxf.线程的方法.Test9 - t1被唤醒
    21:36:48.677  INFO [t2] com.dxf.线程的方法.Test9 - t2被唤醒
    

    二、wait()和sleep()的区别

    • sleep()是Thread类中的方法,wait()是object类中的方法.
    • sleep()可以在任意位置执行,wait()只能在synchronized中获取到对象锁执行.
    • sleep()在synchronized中执行不会释放对象锁,wait()则会进入waitSet队列中等待同时释放锁.

    三、wait()和notify()的正确使用姿势

    大家看下面一段代码:

     static final Object lock = new Object();
    
        static boolean flag = false;
    
        static boolean hasDinner = false;
    
        /**
         * 现在有两个线程
         * t1线程:需要有烟才能干活
         * t2线程:等待外卖,外卖到了开始吃饭
         * @param args
         */
        public static void main(String[] args) {
    
            new Thread(() ->{
                synchronized (lock){
                    while (!flag){
                        //如果flag为false,则进入等待(会释放锁),当为ture时才会往下走
                        try {
                            log.info("{}","没有烟...不能干活...");
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
    
                    log.info("开始干活");
    
                }
    
            },"t1").start();
    
            new Thread(() ->{
                synchronized (lock){
                    while (!hasDinner){
                        //如果flag为false,则进入等待(会释放锁),当为ture时才会往下走
                        try {
                            log.info("{}","等待外卖...");
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
    
                    log.info("外卖到了...开始吃饭...");
    
                }
    
            },"t2").start();
    
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            synchronized (lock){
                hasDinner = true;
                lock.notify();
            }
    
        }
    

    当主线程将送外卖的变量改为ture,表示外卖到了,t2线程应该被唤醒。可如果使用了notify()方法,只能随机唤醒一个在waitSet中的线程,输出结果:

    20:56:33.551  INFO [t1] com.dxf.线程的方法.Test10 - 没有烟...不能干活...
    20:56:33.552  INFO [t2] com.dxf.线程的方法.Test10 - 等待外卖...
    20:56:35.552  INFO [t1] com.dxf.线程的方法.Test10 - 没有烟...不能干活...
    

    我们发现t1线程被唤醒了,并且不能继续往下执行,跟我们想的有偏差,如何解决?
    使用notifyAll().唤醒全部等待的线程,满足条件的会继续执行,不满足的则继续进入等待.

    21:01:35.830  INFO [t1] com.dxf.线程的方法.Test10 - 没有烟...不能干活...
    21:01:35.832  INFO [t2] com.dxf.线程的方法.Test10 - 等待外卖...
    21:01:37.830  INFO [t2] com.dxf.线程的方法.Test10 - 外卖到了...开始吃饭...
    21:01:37.830  INFO [t1] com.dxf.线程的方法.Test10 - 没有烟...不能干活...
    

    相关文章

      网友评论

          本文标题:09给女朋友讲讲并发编程-wait()和notify()

          本文链接:https://www.haomeiwen.com/subject/fbdbaktx.html