【JUC】十四、synchronized进阶

文章目录

  • 1、synchronized
  • 2、synchronized与monitor
  • 3、管程Monitor
  • 4、Q:为什么每个Java对象都可以成为一个锁?
  • 5、小结

1、synchronized

写个demo,具体演示下对象锁与类锁,以及synchronized同步下的几种情况练习分析。demo里有资源类手机Phone,其有三个方法,发短信和发邮件这两个方法有synchronized关键字,另一个普通方法getHello。

class Phone {public synchronized void sendSMS() throws Exception {//TimeUnit.SECONDS.sleep(4);System.out.println("------sendSMS");}public synchronized void sendEmail() throws Exception {System.out.println("------sendEmail");}public void getHello() {System.out.println("------getHello");}
}

然后启动两个线程AA和BB,且二者进入就绪状态中间休眠100ms,给AA一个先抢夺CPU时间片的优势。

public class Lock8 {public static void main(String[] args) throws InterruptedException {Phone phone = new Phone();new Thread(() -> {try {phone.sendSMS();} catch (Exception e) {e.printStackTrace();}},"AA").start();/*** start后线程进入的是就绪状态,即具有抢夺CPU时间片(执行权)的能力,并不是直接执行* 这里刻意休眠100毫秒,让AA线程先去抢时间片,给AA一个先执行的优势*/Thread.sleep(100);new Thread(() -> {try {phone.sendEmail();//phone.getHello();} catch (Exception e) {e.printStackTrace();}},"BB").start();}}

分析以下八种情况的输出结果:

Case1:就上面的代码,直接执行

Case2:sendSMS()方法体加一行TimeUnit.SECONDS.sleep(4),即停留4秒

分析:对于上面两种情况,synchronized出现在示例方法中占的是对象锁,而两线程共用同一个对象,因此先抢时间片的线程AA先执行,而sleep是抱着锁睡,所以输出都是:

------sendSMS
------sendEmail

Case3:BB线程改为调用普通方法getHello

分析:getHello方法不用对象锁,所以不用等,而AA线程的sendSMS要sleep4秒,因此getHello就先输出:

------getHello
------sendSMS

Case4:两个手机Phone对象,分别给AA和BB线程调用两个synchronized方法

分析:两个Phone对象,两个对象锁,各自调synchronized实例方法,没有抢锁和等待的情况,没有sleep的自然先输出:

------sendEmail
------sendSMS

Case5:两个synchronized方法均加static改为静态方法,两线程共用1个Phone资源对象

Case6:两个synchronized方法均加static改为静态方法,两线程分别用2个Phone资源对象

分析:synchronized两个静态方法,锁的就是类锁,即当前类的Class对象,一个类就一把类锁,所以尽管有两个Phone对象在调也没用,先拿到类锁的先执行并输出:

------sendSMS
------sendEmail

Case7:BB线程调用静态同步sendEmail、AA线程调用无static的同步方法sendSMS,两线程共用1个Phone资源对象

Case8:BB线程调用静态同步sendEmail、AA线程调用无static的同步方法sendSMS,两线程分别用2个Phone资源对象

分析:不管1个/2个Phone对象,AA线程用的对象锁,BB线程用的类锁,互不影响,对象锁代码中有sleep,晚输出:

------sendEmail
------sendSMS

总结:

synchronized实现同步时:

  • synchronized加在静态方法上,锁的就是类锁,即当前类的Class对象,一个类就一把类锁
  • synchronized加在实例方法上,锁的是调用该方法的当前对象,是对象锁,一个类创建100个对象,就有100把对象锁
  • synchronized加在代码块上,锁的是synchronized括号里配置的对象
public class LockSyncDemo{Object object = new Object();public void m1(){synchronized (object) {System.out.println("同步代码块,锁object对象");}}}

在这里插入图片描述

2、synchronized与monitor

写个简单Demo:

public class LockSyncDemo {Object object = new Object();public void m1(){synchronized (object){System.out.println("hello synchronized!");}}public static void main(String[] args) {}
}

编译运行后,在target中open in Terminal

在这里插入图片描述

在终端执行javap 对class文件反编译:

javap  -c  ***.class//-c即反汇编,也可-v,即-verbose,输出更详细的附加信息

部分汇编指令如下:

在这里插入图片描述

上面发现,同步代码块上下出来一对monitor enter和exit,最后还有个未配对的monitor exit,这个类比finally中关资源,是为了万一同步代码块中间发生异常,也确保对象锁被释放。PS:并不总是多一个monitor exit,这个也可在上面Demo代码里的同步代码块中加一句异常:

//add
throw new RuntimeException("---exp");

再反编译,就只有一对堆monitor了

在这里插入图片描述

结论:synchronized同步代码块,底层是使用monitorenter和monitorexit指令。

再改为synchronized修饰实例方法:

public void m1(){synchronized (object){System.out.println("hello synchronized!");}
}

在这里插入图片描述

结论:JVM读到ACC_SNCHRONIZED标志位,就知道这是一个同步方法。继续改为静态同步方法,再次汇编:

public static synchronized void m1() {System.out.println("hello synchronized!");
}

在这里插入图片描述

结论:ACC_STATIC,ACC_SYNCHRONIZED访问标志区分该方法是否为静态同步方法

3、管程Monitor

管程,Monitors,也称监视器,一种结构,结构内的共享资源对工作线程会互斥访问(独占,同一时间,用于保证只有一个线程可以访问被保护的数据或代码)。直白说就是锁。

  • JVM中同步的实现就是基于进入和退出监视器对象Monitor或者是管程对象的。每个对象实例都会有一个Monitor对象,Monitor对象也就是管程。
  • Monitor对象会和Java对象一同创建并销毁,底层由C++实现

比如前面同步方法的底层实现,当方法调用时,调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置。如果设置了,执行线程会将先去持有monitor锁(执行线程就要求先成功持有管程),然后再执行方法,最后在方法完成(无论是正常完成还是非正常完成)时释放 monitor。

4、Q:为什么每个Java对象都可以成为一个锁?

为什么任何一个对象都可以成为或者说带有一个锁(对象锁)?

在Java虚拟机HotSpot中,monitor由ObjectMonitor实现,

在这里插入图片描述

其对应在底层c++就是ObjectMonitor.cpp --> ObjectMonitor.hpp:

在这里插入图片描述

Java任何一个类都有Object这个父类,每个对象实例都会有一个Monitor对象,而Monitor对象的_owner中记录了哪个线程持有了ObjectMonitior对象锁。由此,每一个被锁的对象和Monitor对象关联,Monitor对象中又记录了当前持有锁的线程,当一个monitor对象被某个线程持有后,它便处于锁定状态,因此每个Java对象都可以成为一个锁。

总结:每个对象自带Monitor对象,而Monitor对象在c++里有属性owner,里面记录了当前谁持有锁,由此,每个Java对象都可以成为一个锁。

在这里插入图片描述

再补充下各个属性间的关系:

当多个线程同时访问一段同步代码时,首先会进入 _EntryList 集合,当线程获取到对象的monitor 后,进入 _Owner 区域,并把monitor中的_owner变量设置为当前线程,同时monitor中的计数器count加1。

若线程调用 wait() 方法,将释放当前持有的monitor,_owner变量恢复为null,count自减1,同时该线程进入 WaitSet集合中等待被唤醒。

若当前线程正常执行完毕也将释放monitor锁并复位变量的值,以便其他线程进入获取monitor锁。
在这里插入图片描述

5、小结

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/228733.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Notion for Mac:打造您的专属多功能办公笔记软件

在如今这个信息爆炸的时代,一款高效、便捷的笔记软件对于办公人士来说已经成为必不可少的工具。Notion for Mac,作为一款多功能办公笔记软件,凭借其简洁优雅的界面、强大的功能以及无缝的云端同步,成为了众多用户的首选。 一、多…

LD_PRELOAD劫持

LD_PRELOAD劫持 <1> LD_PRELOAD简介 LD_PRELOAD 是linux下的一个环境变量。用于动态链接库的加载&#xff0c;在动态链接库的过程中他的优先级是最高的。类似于 .user.ini 中的 auto_prepend_file&#xff0c;那么我们就可以在自己定义的动态链接库中装入恶意函数。 也…

Redis基本操作及使用

&#x1f4d1;前言 本文主要是【Redis】——Redis基本操作及使用的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每日一…

Spring Boot 3 + Spring Security 6 最新版本修改 Json 登录后 RememberMe 功能问题失效的解决方案

当 Spring Boot 版本更新到 3 之后&#xff0c;最低要求的 JDK 版本变为 17&#xff0c;相应的 最新版本的 Spring Security 的配置也发生了变化&#xff0c;一下主要讲解一些新的 Spring Security 的配置方法 1. 配置由继承WebSeucrityConfigurerAdapter变成只需添加一个Secur…

蓝桥杯第一天-----时间显示

文章目录 前言一、题目描述二、测试用例三、题目分析四、具体代码实现总结 前言 本章中将相信介绍蓝桥杯中关于时间显示的题目。 链接&#xff1a;https://www.lanqiao.cn/problems/1452/learning/ 一、题目描述 二、测试用例 三、题目分析 1.输入的时间为毫秒&#xff0c;毫…

2020年6月15日 Go生态洞察:pkg.go.dev开源探索

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

接口01-Java

接口-Java 一、引入(快速入门案例)二、接口介绍1、概念2、语法 三、应用场景四、接口使用注意事项五、练习题1 一、引入(快速入门案例) usb插槽就是现实中的接口。 你可以把手机、相机、u盘都插在usb插槽上&#xff0c;而不用担心那个插槽是专门插哪个的&#xff0c;原因是做u…

深入理解虚拟 DOM:提升前端性能的关键技术

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

【3D程序软件】SideFX与上海道宁一直为设计师提供程序化 3D动画和视觉效果工具,旨在创造高质量的电影效果

Houdini是一个 从头开始构建的程序系统 使艺术家能够自由工作 创建多次迭代 并与同事快速共享工作流程 Houdini FX为 视觉特效艺术家创作故事片 广告或视频游戏 凭借其基于程序节点的工作流程 Houdini FX可让 您更快地创建更多内容 从而缩短时间并 在所有创意任务中…

STM32 ADC转换器、串口输出

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、ADC是什么&#xff1f;二、STM32的ADC2.1 认识STM32 ADC2.2转换方式2.3 为什么要校准&#xff1f;2.4 采样时间计算2.5 触发方式2.6 多通道采集解决方案2.7…

C++学习之路(十)C++ 用Qt5实现一个工具箱(增加一个时间戳转换功能)- 示例代码拆分讲解

上篇文章&#xff0c;我们用 Qt5 实现了在小工具箱中添加了《JSON数据格式化》功能&#xff0c;还是比较实用的。为了继续丰富我们的工具箱&#xff0c;今天我们就再增加一个平时经常用到的功能吧&#xff0c;就是「 时间戳转换 」功能&#xff0c;而且实现点击按钮后文字进行变…

【计算机网络笔记】以太网

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…