【面试:并发篇16:多线程:wait/notify详解】原理及错误用法(虚假唤醒等)

我们之前学习的过程中浅显的了解过wiat/notify,但是没有系统的介绍过wait/notify,wait是使线程陷入等待notify是随机唤醒一个被wait的线程。obj.wait()wait方法让进入object监视器的线程到waitSet等待。wait后会释放对象锁,让其他线程竞争wait的有时限方法,如果在时限内没有其他线程唤醒,则自己直接唤醒自己,若期间有别的线程唤醒那就正常唤醒。wait后会释放对象锁,让其他线程竞争notify方法让正在waitSet等待的线程挑一个唤醒结果解释。...
摘要由CSDN通过智能技术生成

【面试:并发篇16:多线程:wait/notify详解】原理及错误用法(虚假唤醒等)

00.前言

如果有任何问题,请指出。

01.介绍

我们之前学习的过程中浅显的了解过wiat/notify,但是没有系统的介绍过wait/notify,wait是使线程陷入等待 notify是随机唤醒一个被wait的线程。

02.工作原理

当一个线程获取锁后 但是发现自己不满足某些条件 不能执行锁住部分的代码块时 需要进入等待列表 直到满足条件时才会重新竞争线程

上图为它的工作原理

注意

1.Owner发现条件某个线程不满足条件,调用wait方法,此时这个线程进入WaitSet,并且这个线程的状态变为WAITING状态

2.BLOCKED和WAITING状态的线程都不参与cpu调度,不占用cpu时间片

3.WAITING线程会在Owner线程调用notify或notifyAll时唤醒,但唤醒后仍然要进入EntryList重新竞争锁

03.API介绍

obj.wait():wait方法让进入object监视器的线程到waitSet等待。wait后会释放对象锁,让其他线程竞争

obj.wait(Long timeout):wait的有时限方法,如果在时限内没有其他线程唤醒,则自己直接唤醒自己,若期间有别的线程唤醒那就正常唤醒。wait后会释放对象锁,让其他线程竞争

obj.notify():notify方法让正在waitSet等待的线程挑一个唤醒

obj.notifyAll():notifyAll方法让正在waitSet等待的线程全部唤醒

它们都是线程之间进行协作的手段,都属于Object对象方法,必须获取此对象的锁,才能调用这几个方法,如果不加锁直接调用这些方法会报错

notify与notifyAll的对比

@Slf4j(topic = "c.TestWaitNotify")
public class TestWaitNotify {
   
    final static Object obj = new Object();

    public static void main(String[] args) {
   

        new Thread(() -> {
   
            synchronized (obj) {
   
                log.debug("执行....");
                try {
   
                    obj.wait(); // 让线程在obj上一直等待下去
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }
                log.debug("其它代码....");
            }
        },"t1").start();

        new Thread(() -> {
   
            synchronized (obj) {
   
                log.debug("执行....");
                try {
   
                    obj.wait(); // 让线程在obj上一直等待下去
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }
                log.debug("其它代码....");
            }
        },"t2").start();

        // 主线程两秒后执行
        sleep(0.5);
        log.debug("唤醒 obj 上其它线程");
        synchronized (obj) {
   
//            obj.notify(); // 唤醒obj上一个线程
            obj.notifyAll(); // 唤醒obj上所有等待线程
        }
    }
}

结果

调用notify时:

23:11:55.798 c.TestWaitNotify [t1] - 执行…
23:11:55.801 c.TestWaitNotify [t2] - 执行…
23:11:56.300 c.TestWaitNotify [main] - 唤醒 obj 上其它线程
23:11:56.300 c.TestWaitNotify [t1] - 其它代码…

调用notifyAll时:

23:12:26.195 c.TestWaitNotify [t1] - 执行…
23:12:26.198 c.TestWaitNotify [t2] - 执行…
23:12:26.699 c.TestWaitNotify [main] - 唤醒 obj 上其它线程
23:12:26.699 c.TestWaitNotify [t2] - 其它代码…
23:12:26.699 c.TestWaitNotify [t1] - 其它代码…

解释

可以看出notify是随机唤醒一个线程,notifyAll则是唤醒全部线程

04.wait与sleep方法的区别

区别

1.sleep是Thread的类方法,而wait是Object的对象方法

2.sleep不需要强制和synchronized配合使用,但是wait需要和synchronized一起用

3.sleep在睡眠的同时,不会释放对象锁,但wait在等待时会释放对象锁

4.无时限wait方法执行后 线程变为WAITING状态,有时限的wait方法与sleep方法执行后变为TIMED_WAITING状态

分析下面代码

@Slf4j(topic = "c.Test19")
public class Test19 {
   

    static final Object lock = new Object();
    public static void main(String[] args) {
   
        new Thread(() -> {
   
            synchronized (lock) {
   
                log.debug("获得锁");
                try {
   
//                    Thread.sleep(2000);
                    lock.wait(2000);
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }
            }
        }, "t1").start();

        Sleeper.sleep(1);
        synchronized (lock) {
   
            log.debug("获得锁");
        }
    }
}

结果

当调用sleep时的情况:

23:20:48.788 c.Test19 [t1] - 获得锁

当调用wait时的情况:

23:21:27.759 c.Test19 [t1] - 获得锁
23:21:28.768 c.Test19 [main] - 获得锁

解释

上述结果说明sleep在暂停期间 不会释放锁 导致 这期间其他线程不能运行,而wait则可以释放锁

05.wait/notify的正确使用情况

既然是正确的使用情况,那就需要一步一步来,把不正确的部分逐渐优化。

例子说明

现在有一群人需要干活,其中一个人叫做小南 他必须吸烟时才能干活。现在就是针对这个问题进行模拟。

模拟一

import lombok.extern.slf4j.Slf4j;
@Slf4j(topic = "c.TestCorrectPosture")
public class TestCorrectPostureStep1 {
   
    static final Object room = new Object(
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

I cream

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值