• Java多线程:waitnotify原理剖析


    Java多线程wait/notify原理

    一、引入wait/notify

    回顾:在之前的学习中,当我们创建一个对象后,synchronized给对象上锁,JVM会给对象头关联一个Monitor对象,这个Monitor由三部分组成。
    一是Owner对象,里面存储的是创建该对象的线程
    二是EntryList,想试图获取该对象资源的其它堵塞线程队列
    三是WaitSet,存储的是放弃对象锁的线程

    在这里插入图片描述

    • Owner线程中的锁对象,如果发现条件不满足,调用wait()方法,既可以进入到WaitSet变为WAITING状态
    • EntryList下和WaitSet下的线程都属于堵塞状态,不占用CPU时间片
    • EntryList下的线程会在Owner是释放锁时被唤醒
    • WaitSet下的线程会在Owner线程调用notify或notifyAll时被唤醒,但是唤醒后并不意味立刻获得锁,需要进入EntryList重新竞争锁

    API介绍

    • wait() 会让obj对象由拥有锁到暂时放弃锁,进入到waitset中
    • notify() 会唤醒在waitset中的线程,然后需要进入EntryList重新竞争锁
    • notifyAll() 会唤醒所有在waitset中的线程,然后需要进入EntryList重新竞争锁

    wait(long n)wait()的区别是,前者是有时限的等待,时间到后自己进入EntryList中,后者如果没有notify()notifyAll()唤醒则进入永久等待。

    wait/notify 的正确姿势

    开始之前先看看sleep()wait()方法有什么区别?

    • sleep()是Thread的静态方法,而wait()方法是Object类下的方法
    • sleep()方法不需要与synchronized配合使用,但是wait()方法必须和sysnchronized配合才能使用
    • 调用sleep()方法,对象不会释放锁,调用wait()方法时对象会释放锁
    • 相同点就是调用两个方法后,线程的状态都是TIMED_WAITING状态

    wait/notify与锁关系的探究

    wait()时是否需要持有锁? notify()是否需要持有锁?先说答案:都需要持有锁。

    wait需要持有锁的原因是,你肯定需要知道在哪个对象上进行等待,如果不持有锁,将无法做到对象变更时进行实时感知通知的作用。与此同时,为了让其他线程可以操作该值的变化,它必须要先释放掉锁,然后在该节点上进行等待。不持有锁而进行wait,可能会导致长眠不起。而且,如果不持有锁,则当wait之后的操作,都可能是错的,因为可能这个数据已经过时,其实也叫线程不安全了。总之,一切为了安全,单独的wait做不成这事。

    notify需要持有锁的原因是,它要保证线程的安全,只有它知道数据变化了,所以它有权力去通知其他线程数据变化。而且通知完之后,不能立即释放锁,即必须在持有锁的情况下进行通知,否则notify后续的工作的线程安全性将无法保证,尽量它是在lock的范围内,但却因为锁释放,将导致不可预期的结果。而且在notify的时候,并不能真正地将对应的线程唤醒,即不能从操作系统层面唤醒线程,因为此时当前通知线程持有锁,而此时如果将其他等待线程唤醒,它们将立即参与到锁的竞争中来,而这时的竞争是一定会失败的,这可能会导致被唤醒的线程立即又进入等待队列,更糟糕的是它可能再也不会被唤醒 了。所以不能将在持有锁的时,将对应的线程真正唤醒,我们看到的notify只是从语言上下文级别,将它从等待队列转移到同步队列而已(就是从上面的WaitSet进入EntryList),对此操作系统一无所知

  • 相关阅读:
    Oracle 常用命令大全
    【Spring笔记05】Spring的自动装配
    cookie 里面都包含什么属性?
    Mac M3 芯片安装 Nginx
    服务效率飙升!2024最新Zoho Desk功能解析
    Tomcat10下载安装及各个线程作用详解
    Kotlin 不可变数组和可变数组
    mount -o bind
    P 进阶_(eval 函数)
    springboot基于SpringBoot的冬奥会科普平台springboot21
  • 原文地址:https://blog.csdn.net/Urbanears/article/details/128121104