13. 大佬问我: notify()会立刻释放锁么?

Object 中的 wait() 与 notify() 方法有什么用? wait()方法和notify()方法是用于线程间的通信和同步的方法。由于线程之间是抢占式执行的,因此线程之间执行的先后顺序难以预知。但是有时候我们希望合理的协调多个线程之间的执行顺序。这时候就需要用到 Object 中的 wait()方法和notify()方法了。 阅读详情

大佬问我: notify()会立刻释放锁么?

​

我的内心戏: 肯定会啊! 这么简单的问题? image

聪明如我, 决定装小白, 回答: 不会?

大佬: 很好, 小伙子基础不错!

我: image

大佬: 说说为什么

我: ………………image

于是, 有了这篇文章!

问题的根本原来在于 “立刻”这个描述词!

如果你和咸鱼君一样懵逼, 不妨往下看!

技术大佬可以告辞了!!

接下来, 我们深入的分析分析wait和notify

前言

前面介绍了Synchronized关键词的原理与优化分析,Synchronized的重要不言而喻, 而作为配合Synchronized使用的另外两个关键字也显得格外重要.

今天, 来聊聊配合Object基类的

  • wait()

  • notify()

这两个方法的实现,为多线程协作提供了保证。

wait() & notify()

Object 类中的 wait&notify 这两个方法,其实包括他们的重载方法一共有 5 个,而 Object 类中一共才 12 个方法,可见这 2 个方法的重要性。

我们先看看 JDK 中的定义:

public final native void notify();

其中有 3 个方法是 native 的,也就是由虚拟机本地的 c 代码执行的。

ps: native 即 JNI,Java Native Interface,

Java平台提供的用户和本地C代码进行互操作的API

有 2 个 wait 重载方法最终还是调用了 wait(long)方法。

wait方法

wait是要释放对象锁,进入等待池。
既然是释放对象锁,那么肯定是先要获得锁。
所以wait必须要写在synchronized代码块中,否则会报异常。

notify方法

也需要写在synchronized代码块中,
调用对象的这两个方法也需要先获得该对象的锁.
notify,notifyAll, 唤醒等待该对象同步锁的线程,并放入该对象的锁池中.
对象的锁池中线程可以去竞争得到对象锁,然后开始执行.
​
如果是通过notify来唤起的线程,
那进入wait的线程会被随机唤醒;
(注意: 实际上, hotspot是顺序唤醒的!! 这是个重点! 有疑惑的点击传送大佬问我: notify()是随机唤醒线程么?
)

​

如果是通过nootifyAll唤起的线程,
默认情况是最后进入的会先被唤起来,即LIFO的策略;

比较重要的是:

notify()或者notifyAll()调用时并不会真正释放对象锁, 必须等到synchronized方法或者语法块执行完才真正释放锁.

举个例子:

public void test()
{
    Object object = new Object();
    synchronized (object){
        object.notifyAll();
        while (true){
        }
    }
}

如上, 虽然调用了notifyAll, 但是紧接着进入了一个死循环。

这会导致一直不能出临界区, 一直不能释放对象锁。

所以,即使它把所有在等待池中的线程都唤醒放到了对象的锁池中,

但是锁池中的所有线程都不会运行,因为他们始终拿不到锁。

案例分析

为了说明wait() 和notify()方法的功能,

我们举个例子

public class WaitNotifyCase {
​
public static void main(String[] args) {
  final Object lock = new Object();
​
  new Thread(new Runnable() {
      @Override
      public void run() {
          System.out.println("线程 A 等待 获得 锁");
          synchronized (lock) {
              try {
                  System.out.println("线程 A 获得 锁");
                  TimeUnit.SECONDS.sleep(1);
                  System.out.println("线程 A 开始 执行 wait() ");
                  lock.wait();
                  System.out.println("线程 A 结束 执行 wait()");
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }
  }).start();
​
  new Thread(new Runnable() {
      @Override
      public void run() {
          System.out.println("线程 B 等待 获得 锁");
          synchronized (lock) {
              System.out.println("线程 B 获得 锁");
              try {
                  TimeUnit.SECONDS.sleep(5);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              lock.notify();
              System.out.println("线程 B 执行 notify()");
          }
      }
  }).start();
}
}

执行结果:

线程 A 等待 获得 锁
线程 A 获得 锁
​
线程 B 等待 获得 锁
​
线程 A 开始 执行 wait()
​
线程 B 获得 锁
线程 B 执行 notify()
​
线程 A 结束 执行 wait()

使用时切记:必须由同一个lock对象调用wait、notify方法

  • 当线程A执行wait方法时,该线程会被挂起;

  • 当线程B执行notify方法时,会唤醒一个被挂起的线程A;

lock对象、线程A和线程B三者是一种什么关系?

根据上面的案例,可以想象一个场景:

  • lock对象维护了一个等待队列list;

  • 线程A中执行lock的wait方法,把线程A保存到list中;

  • 线程B中执行lock的notify方法,从等待队列中取出线程A继续执行;

几个疑问

问题一: 为何wait&notify必须要加synchronized锁?

从实现上来说,这个synchronized锁至关重要!

正因为这把锁,才能让整个wait/notify运转起来.

当然我觉得其实通过其他的方式也可以实现类似的机制,

不过hotspot至少是完全依赖这把锁来实现wait/notify的.

static void Sort(int [] array) {
    // synchronize this operation so that some other thread can't
    // manipulate the array while we are sorting it. This assumes that other
    // threads also synchronize their accesses to the array.
    synchronized(array) {
        // now sort elements in array
    }
}


synchronized代码块通过javap生成的字节码中包含monitorenter 和 monitorexit 指令

如下图所示:

image

执行monitorenter指令可以获取对象的monitor,

而lock.wait()方法通过调用native方法wait(0)实现,其中接口注释中有这么一句:

The current thread must own this object’s monitor.

表示线程执行 lock.wait() 方法时,必须持有该lock对象的monitor.

问题二: 为什么wait方法可能抛出InterruptedException异常?

这个异常大家应该都知道,当我们调用了某个线程的interrupt方法时,对应的线程会抛出这个异常;

wait方法也不希望破坏这种规则,

因此就算当前线程因为wait一直在阻塞,当某个线程希望它起来继续执行的时候,它还是得从阻塞态恢复过来;

而wait方法被唤醒起来的时候会去检测这个状态,当有线程interrupt了,它就会抛出这个异常从阻塞状态恢复过来。

这里有两点要注意:

  1. 如果被interrupt的线程只是创建了,并没有start,那等他start之后进入wait态之后也是不能会恢复的;

  2. 如果被interrupt的线程已经start了,在进入wait之前,如果有线程调用了其interrupt方法,那这个wait等于什么都没做,会直接跳出来,不会阻塞;

问题三: notify执行之后立马唤醒线程吗?

其实hotspot里真正的实现是: 退出同步块的时候才会去真正唤醒对应的线程; 不过这个也是个默认策略,也可以改的,在notify之后立马唤醒相关线程。

问题四: notifyAll是怎么实现全唤起所有线程?

或许大家立马就能想到一个for循环就搞定了,不过在JVM里没实现这么简单,而是借助了monitorexit.

上面提到了当某个线程从wait状态恢复出来的时候,要先获取锁,然后再退出同步块;

所以notifyAll的实现是调用notify的线程在退出其同步块的时候唤醒起最后一个进入wait状态的线程;

然后这个线程退出同步块的时候继续唤醒其倒数第二个进入wait状态的线程,依次类推.

同样这这是一个策略的问题,JVM里提供了挨个直接唤醒线程的参数,不过很少使用, 这里就不提了。

问题五: wait的线程是否会影响性能?

这是个大家比较关心的话题.

wait/nofity 是通过JVM里的 park/unpark 机制来实现的,在Linux下这种机制又是通过pthread_cond_wait/pthread_cond_signal 来实现的;

因此当线程进入到wait状态的时候其实是会放弃cpu的,也就是说这类线程是不会占用cpu资源。

欢迎关注我

技术公众号 “CTO技术”

订阅号.png

Object类中的wait()和notify() 目录前言线程之间的通信实现`Thread` 类的 `join()` 前言 线程一旦开始运行,就会拥有自己独立的栈空间。正常情况下,每个子线程完成各自的任务就可以结束了。不过有的时候,我们希望多个线程协同工作来完成某个任务,这时就涉及到了线程间通信了 线程间的通信:使线程间能够互相发送信号,等待其他线程的信号。比如线程 A 在执行到某个条件时通知线程 B 执行某个操作 线程之间的通信实现 Thread 类的 join() public final void join() throws InterruptedE 阅读详情

相关推荐

notify()真的是随机唤醒嘛?

notify()的唤醒机制 文章目录notify()的唤醒机制前言notify() 前言 学过java的小伙伴都知道,我们在学习过程中,在学习并发多线程时,有一个notify()方法,当时博主在学习的时候,是说notify()随机唤醒一个等待中的线程并获取锁。相信许多小伙伴和博主一样,但是事实真的是这样吗? notify() 先上一段简单的线程wait()与notify()方法。方法的逻辑就不用我讲了吧。。。 import java.util.LinkedList; import java.util.L

m0_50348316的博客 976

java JUC 中 Object里wait()、notify() 实现原理及实战讲解

在多核环境下,多个线程有可能同时执行monitorenter指令,并获取lock对象关联的monitor,但只有一个线程可以和monitor建立关联,这个线程执行到wait方法时,wait方法会将当前线程放入wait set,使其进行等待直到被唤醒,并放弃lock对象上的所有同步声明,意味着该线程释放了锁,其他线程可以重新执行加锁操作,notify方法会选择wait set中任意一个线程进行唤醒,notifyAll方法会唤醒monitor的wait set中所有线程。4、最终底层的park方法会挂起线程。

LoveSummer 5万+

从‘最长白列’到‘八邻域’:智能车图像巡线算法怎么选?我的踩坑经验分享

本文深入探讨了全国大学生智能汽车竞赛中图像巡线算法的选型与优化,对比了最长白列法、八邻域算法等五种主流方法的性能与适用场景。通过实战数据和代码示例,分析了各算法在直道、弯道及特殊赛道元素中的表现,并提供了内存优化、参数调整等实用技巧,帮助参赛团队提升智能车的巡线精度与效率。

weixin_30374009的博客 632

Object类九大方法之wait方法

Object类九大方法之wait方法 wait、notify和notifyAll方法是Object类的final native方法。所以这些方法不能被子类重写,Object类是所有类的超类,因此在程序中有以下三种形式调用wait等方法。 wait();//方式1: this.wait();//方式2: super.wait();//方式3 void notifyAll() 解除所有那些在该...

TimeFriends 6563

ROS Noetic下Gazebo仿真:从零搭建两轮差速机器人(含ros_control配置避坑指南)

本文详细介绍了在ROS Noetic环境下,使用Gazebo从零搭建两轮差速机器人并进行仿真的完整流程。核心内容包括利用Xacro进行模块化建模,并重点解析了通过ros_control框架配置机器人运动控制的详细步骤与常见避坑指南,帮助开发者成功实现机器人在仿真环境中的差速驱动与运动控制。

game4的博客 619

基于YOLOv8的多类别检测模型融合实战:从数据集重构到模型训练

本文详细介绍了如何将多个单一类别的YOLOv8检测模型融合为一个高效的多类别检测模型。通过重构数据集、利用预训练模型生成新标签,并重新训练,解决了多模型并行带来的速度慢、资源占用高和部署复杂等问题。实战流程涵盖数据准备、标签生成、模型训练与评估,旨在帮助开发者构建性能优异的YOLOv8多类别检测模型。

kmeans3miner的博客 227

13. 大佬问我: notify()会立刻释放锁么?

大佬问我:notify()会立刻释放锁么? ​ 我的内心戏: 肯定会啊! 这么简单的问题? image 聪明如我, 决定装小白, 回答:不会? 大佬:很好, 小伙子基础不错! 我: image 大佬:说说为什么 我: ……………… image 于是, 有了这篇文章! 问题的根本原来在于 “立刻”这个描述词! 如果你和咸鱼君一样懵逼, 不妨往下看! 技术大佬可以告辞了!! 接下来, 我们深入的分析分析wait和noti...

u011250186的博客 349

新手避坑指南:用PIC16F15355开发板和MCC完成第一个UART通信项目

本文为新手提供了使用PIC16F15355开发板和MPLAB代码配置器(MCC)实现首个UART通信项目的完整指南。通过图形化配置系统时钟、UART参数并自动生成驱动代码,手把手教学如何避开常见陷阱,快速完成从环境搭建、代码编写到调试输出的全流程,助力开发者高效入门PIC单片机开发。

bash7scripter的博客 725

Java线程基础(13):wait()和notify()

简介 本文讲解Java中wait()、notify(),通过一个标准的使用实例,来讨论下这两个方法的作用和使用时注意点,这两个方法被提取到顶级父类Object对象中,地位等同于toString()方法。 一、wait()和notify()含义 wait()方法是让当前线程等待的,即让线程释放了对共享对象的锁,不再继续向下执行。 wait(long timeout)方法可以指定一个超时时间,过了这个时间如果没有被notify()唤醒,则函数还是会返回。如果传递一个负数timeout会抛出I...

mingyuli的博客 1万+

28GHz毫米波滤波器设计实战:用SynMatrix快速搞定SIW带通滤波器(附完整参数)

本文详细介绍了如何使用SynMatrix工具链高效设计28GHz SIW带通滤波器,涵盖智能参数预测、多物理场联合优化和加工误差补偿等核心技术。通过实战案例和完整参数模板,帮助射频工程师快速实现毫米波滤波器设计,显著提升工作效率并避免反复迭代。

weixin_29284657的博客 495

手把手教学:利用AutoDL与vLLM快速部署DeepSeek-R1大模型实战指南

本文详细介绍了如何利用AutoDL平台和vLLM框架快速部署DeepSeek-R1大模型的实战指南。从服务器配置、vLLM安装优化到模型部署的两种方案,再到性能调优和生产环境建议,手把手教你高效完成大模型部署。特别针对国内开发者遇到的网络问题提供了稳定解决方案,并分享了显存优化、磁盘清理等实用技巧。

weixin_42531779的博客 326

Join()会不会释放锁?

1、看视频进行系统学习这几年的Crud经历,让我明白自己真的算是菜鸡中的战斗机,也正因为Crud,导致自己技术比较零散,也不够深入不够系统,所以重新进行学习是很有必要的。我差的是系统知识,差的结构框架和思路,所以通过视频来学习,效果更好,也更全面。关于视频学习,个人可以推荐去B站进行学习,B站上有很多学习视频,唯一的缺点就是免费的容易过时。2、读源码,看实战笔记,学习大神思路“编程语言是程序员的表达的方式,而架构是程序员对世界的认知”。所以,程序员要想快速认知并学习架构,读源码是必不可少的。

2401_84049040的博客 777

微信小程序 - 最新获取用户昵称 / 头像(wx.getUserProfile 接口被废弃后的代替方案)详细教程,2022 年之后的所有微信小程序,获取用户信息最新详细教程,附带示例源代码

微信小程序wx.getUserProfile接口废了用什么代替,微信小程序wx.getUserProfile不能用了怎么办,wx.getUserProfile失效有什么替代方案吗,微信新规定头像昵称填写最新教程,目前微信小程序最新获取用户头像昵称教程,微信小程序用户头像昵称获取规则调整公告,2022改版后的微信小程序如何获取用户头像和昵称信息,微信小程序官方废弃getUserProfile后如何实现获取用户信息,微信小程序wx.getUserProfile接口返回了默认头像昵称,微信小程序获取用户信息不

🏆 前端领域优质创作者 2万+

JavaWeb~线程之间的通信 对象的等待集wait set -- wait() 、notify()

针对最近很多人都在面试,我这边也整理了相当多的面试专题资料,也有其他大厂的面经。希望可以帮助到大家。下面的面试题答案都整理成文档笔记。也还整理了一些面试资料&最新2021收集的一些大厂的面试真题(都整理成文档,小部分截图)最新整理电子书《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!厂的面试真题(都整理成文档,小部分截图)[外链图片转存中…(img-3rY39kt6-1712042457135)]最新整理电子书。

2401_84048179的博客 412

【第24例】华为 IPD 体系 | RMT 需求管理团队

RMT需求管理团队

产品线负责人 1763

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

wait() 与 notify/notifyAll()

wait() 与 notify/notifyAll() 是Object类的方法 1. wait() 与notify/notifyAll方法必须在同步代码块中使用 在执行以上方法时,要先获得锁。那么怎么获得锁呢? 使用synchronized关键字获得锁。因此,wait() 与 notify/notifyAll() 经常与synchronized搭配使用, 即在synchronize...

bingguang1993的博客 442

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

java 多线程 notify_Java多线程:wait()和notify()方法详解

通过前面的内容我们知道,当一个线程正在执行某个对象中被synchronized关键字修饰的方法时,这个对象锁上,其他线程必须等到当前线程执行完后才能访问。这个整个过程可以用下图来表示一个正在运行的线程想要执行synchronized方法,必须要获得当前对象的锁,如果没有获得则当前对象的锁则会被阻塞在对象锁池当中,直到前一个线程释放了该对象锁,当前线程重新获取到对象锁时,线程恢复到可运行状态,当cp...

weixin_39846186的博客 334

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

你真的知道Java同步锁何时释放?

学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!最后再分享的一些BATJ等大厂20、21年的面试题,把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,上面只是以图片的形式给大家展示一部分。Mybatis面试专题MySQL面试专题并发编程面试专题《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!试专题。

2401_84048554的博客 677

wait()和notify()的理解与使用

void notify() Wakes up a single thread that is waiting on this object’s monitor. 译:唤醒在此对象监视器上等待的单个线程 void notifyAll() Wakes up all threads that are waiting on this object’s monitor. 译:唤醒在...

weixin_34148456的博客 198

锁标记如果过多,就会出现线程等待其他线程释放锁标记

锁标记如果过多,就会出现线程等待其他线程释放锁标记,而又都不释放自己的锁标记供其他线程运行的状况。就是死锁。 死锁的问题通过线程间的通信的方式进行解决。 线程间通信机制实际上也就是协调机制。 线程间通信使用的空间称之为对象的等待队列,则个队列也是属于对象的空间的。 Object类中又一个wait(),在运行状态中,线程调用wait(),此时表示着线程将释放自己所有的锁标记,同时进...

dengjuanshou7445的博客 196

线程状态-锁-编译器优化-wait-notify-notifyAll

当你经过七重的孤独,才能够成为真正的强者。

2502_91083160的博客 1139

Java多线程学习之wait、notify/notifyAll 详解

1、wait()、notify/notifyAll() 方法是Object的本地final方法,无法被重写。 2、wait()使当前线程阻塞,前提是 必须先获得锁,一般配合synchronized 关键字使用,即,一般在synchronized 同步代码块里使用 wait()、notify/notifyAll() 方法。 3、 由于 wait()、notify/notifyAll() 在syn...

一只苟延残喘的卑微蝼蚁 371
上一篇: 42. 从零开始学springboot整合alibaba-nacos做注册/配置中心
下一篇: 43. 从零开始学springboot整合feign跨服务调用
码哥说
博客等级 码龄8年 5645粉丝 172原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

码哥说

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

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

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

打赏作者

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

抵扣说明:

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

余额充值