【JUC】Synchronized及JVM底层原理

news2024/11/26 1:33:50

Synchronized使用方式

Synchronized有三种应用方式

  • 作用于实例方法,当前示实例加锁进入同步代码前要获得当前实例的锁,即synchronized普通同步方法,调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置。
    如果设置了,执行线程会将先持有monitor然后再执行方法,
    最后在方法完成(无论是正常完成还是非正常完成)时释放 monitor
  • 作用于代码块,对括号里面配置的对象加锁,即synchronized同步代码块,实现使用的是monitorenter和monitorexit指令
  • 作用于静态方法,当前类加锁,进去同步代码前要获得当前对象的锁,即synchronized静态同步方法,ACC_STATIC, ACC_SYNCHRONIZED访问标志区分该方法是否静态同步方法

synchronized同步代码块里面一定是一个enter两个exit吗?

不一定,方法里面添加异常的话会是1-1

在这里插入图片描述

实现原理

Monitor

管程 (英语:Monitors,也称为监视器) 是一种程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源。
这些共享资源一般是硬件设备或一群变量。对共享变量能够进行的所有操作集中在一个模块中。(把信号量及其操作原语“封装”在一个对象内部)管程实现了在一个时间点,最多只有一个线程在执行管程的某个子程序。管程提供了一种机制,管程可以看做一个软件模块,它是将共享的变量和对于这些共享变量的操作封装起来,形成一个具有一定接口的功能模块,进程可以调用管程来实现进程级别的并发控制。

Monitor其实是一种同步机制,他的义务是保证只有一个线程可以访问被保护的数据和代码。JVM中的同步是基于进入和退出监视器对象来实现的,每一个对象实例都会有一个Monitor对象,Monitor对象会和Java对象一同创建并销毁,底层由C++实现

Java虚拟机可以支持方法级的同步和方法内部一段指令序列的同步,这两种同步结构都是使用管程(Monitor,更常见的是直接将它称为“锁”)来实现的。

方法级的同步是隐式的,无须通过字节码指令来控制,它实现在方法调用和返回操作之中。虚拟机可以从方法常量池中的方法表结构中的ACC_SYNCHRONIZED访问标志得知一个方法是否被声明为同步方法。当方法调用时,调用指令将会检查方法ACC_SYNCHRONIZED访问标志是否被设置,如果设置了,执行线程就要求先成功持有管程,然后才能执行方法,最后当方法完成((无论是正常完成还是非正常完成)时释放管程。在方法执行期间,执行线程持有了管程,其他任何线程都无法再获取到同一个管程。如果一个同步方法执行期间抛出了异常,并且在方法内部无法处理此异常,那这个同步方法所持有的管程将在异常抛到同步方法边界之外时自动释放。

同步一段指令集序列通常是由Java语言中的synchronized语句块来表示的,Java虚拟机的指令集中有monitorenter和monitorexit两条指令来支持synchronized关键字的语义,正确实现synchronized关键字需要Javac编译器与Java虚拟机两者共同协作支持,

在HotSpot虚拟机中,monitor采用ObjectMonitor实现。ObjectMonitor.java→ObjectMonitor.cpp→objectMonitor.hpp
!在这里插入图片描述

  • _owner:指向持有ObjectMonitor对象的线程
  • _WaitSet:存放处于wait状态的线程队列,即调用wait()方法的线程
  • _EntryList:存放处于等待锁block状态的线程队列
  • _count:约为_WaitSet 和 _EntryList 的节点数之和
  • _cxq: 多个线程争抢锁,会先存入这个单向链表
  • _recursions: 记录重入次数

Synchronized关键字在编译成class文件后,如果是方法块使用,会在方法块开始和结束的JVM指令之间插入monitorenter和monitorexit指令,如果作用在方法上,会将方法标志位ACC_SYNCHORONIZED,JVM编译时会自动添加上述指令。

每个对象都存在着一个 Monitor对象与之关联。执行 monitorenter 指令就是线程试图去获取 Monitor 的所有权,抢到了就是成功获取锁了;执行 monitorexit 指令则是释放了Monitor的所有权。

Monitor与java对象以及线程是如何关联 ?
1.如果一个java对象被某个线程锁住,则该java对象头的Mark Word字段中LockWord指向monitor的起始地址
2.Monitor的Owner字段会存放拥有相关联对象锁的线程id

java的线程是映射到操作系统原生线程之上的,如果要阻塞或唤醒一个线程就需要操作系统介入,需要在户态与核心态之间切换,这种切换会消耗大量的系统资源,因为用户态与内核态都有各自专用的内存空间,专用的寄存器等,用户态切换至内核态需要传递给许多变量、参数给内核,内核也需要保护好用户态在切换时的一些寄存器值、变量等,以便内核态调用结束后切换回用户态继续工作。

在Java早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的Mutex Lock来实现的,挂起线程和恢复线程都需要转入内核态去完成,阻塞或唤醒一个Java线程需要操作系统切换CPU状态来完成,这种状态切换需要耗费处理器时间,如果同步代码块中内容过于简单,这种切换的时间可能比用户代码执行的时间还长”,时间成本相对较高,这也是为什么早期的synchronized效率低的原因
Java 6之后,为了减少获得锁和释放锁所带来的性能消耗,引入了轻量级锁和偏向锁

synchronized用的锁是存在Java对象头里的Mark Word中
锁升级功能主要依赖MarkWord中锁标志位和释放偏向锁标志位

可重入原理

每个monitor对象拥有一个锁计数器和一个指向持有该锁的线程的指针。当执行monitorenter时,如果目标monitor对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1。在目标monitor对象的计数器不为零的情况下,如果monitor对象的持有线程是当前线程,那么 Java 虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁。当执行monitorexit时,Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。

JVM底层

管程是一种程序结构,程把信号量及其操作原语“封装”在一个对象内部,实现了在一个时间点,最多只有一个线程在执行管程的某个子程序。

Monitor依赖于底层的操作系统的Mutex Lock,也是一种同步机制,保证只有一个线程可以访问被保护的数据和代码。

java中每一个对象实例都会有一个Monitor对象,Monitor对象会和Java对象一同创建并销毁,JVM中的synchronized是基于进入和退出监视器对象来实现的,分别对应两个jvm指令:monitorenter和monitorexit,实现逻辑由底层由C++的ObjectMonitor.cpp实现

monitorenter由ObjectMonitor::enter实现,
在这里插入图片描述

首先尝试通过 CAS 把 ObjectMonitor 中的 _owner 设置为当前线程,设置成功就表示获取锁成功。(通过 _recursions 的自增来表示重入)。

如果没有CAS成功,那么就开始启动自适应自旋,自旋还不行的话,就包装成 ObjectWaiter 对象加入到 _cxq(存放竞争锁的的线程) 单向链表之中。

加入_cxq链表后,再次尝试是否可以CAS拿到锁,再次失败就要阻塞(block),底层调用了pthread_mutex_lock。

monitorexit由ObjectMonitor::exit()实现,

在这里插入图片描述

解锁过程会先判断_recursions字段是否是0,如果不是说明是重入锁,字段--即可;如果等于根据不同的策略决定线程节点放在那里,然后唤醒等待队列中的线程。

线程解锁后还会唤醒之前等待的线程。当线程执行 Object.notify()方法时,从 _waitSet 头部拿线程节点,然后根据策略(QMode指定)决定将线程节点放在哪里,包括_cxq 或 _EntryList 的头部或者尾部,然后唤醒队列中的线程。

可重入锁就是根据 _recursions 来判断的,重入一次就执行 _recursions++,解锁一次就执行 _recursions--,如果 _recursions 减到 0 ,就说明需要释放锁了。

对于方法级的实现是隐式的,当方法调用时,调用指令前会检查方法ACC_SYNCHRONIZED访问标志是否被设置,如果设置了,执行线程就要求先成功持有管程即执行monitorenter,然后才能执行方法,最后当方法完成时释放管程。在方法执行期间,执行线程持有了管程,其他任何线程都无法再获取到同一个管程。

对于代码块方法实现的锁会在代码块中植入monitorenter和monitorrexit这两个JVM指令来实现的,如果代码块中显示抛出异常,那么就只会生成一个moniotrenter的一个monitorexit,如果没有就会生成两个monitorexit,一个用于正常执行释放,一个用于异常执行释放。

当一个线程monitorenter执行成功就会将该对象的对象头的MarkWord字段中的LockWord指向相应monitor对象的起始地址,并且该monitor对象的owner字段会存放该线程的id,count计数器+1和对其他变量的操作,执行monitorexit就是将变量复位或者其他变量操作。

对于synchronized锁升级: 无锁-》偏向锁-》轻量级锁-》重量级锁

synchorized.cpp

//偏向锁
static void fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias,TRAPS);
static void fast_exit(oop obj, BasicLock* lock, Thread* THREAD);

//轻量级
static void slow_enter(Handle obj, BasicLock* lock, TRAPS);
static void slow_exit(oop obj, BasicLock* lock, Thread* THREAD);

//重量级锁
static void jni_enter(Handle obj, TRAPS);
static void jni_exit(oop obj, Thread* THREAD);

synchronized锁升级过程总结:一句话,就是先自旋,不行再阻塞。
实际上是把之前的悲观锁(重量级锁)变成在一定条件下使用偏向锁以及使用轻量级(自旋锁CAS)的形式

偏向锁:适用于单线程适用的情况,在不存在锁竞争的时候进入同步方法/代码块则使用偏向锁。
轻量级锁:适用于竞争较不激烈的情况(这和乐观锁的使用范围类似), 存在竞争时升级为轻量级锁,轻量级锁采用的是自旋锁,如果同步方法/代码块执行时间很短的话,采用轻量级锁虽然会占用cpu资源但是相对比使用重量级锁还是更高效。
重量级锁:适用于竞争激烈的情况,如果同步方法/代码块执行时间很长,那么使用轻量级锁自旋带来的性能消耗就比使用重量级锁更严重,这时候就需要升级为重量级锁。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring相关重点API

一:ApplicationContext的继承体系 ApplicationContext:接口类型,代表应用上下文,可以通过其实例获得Spring容器中的Bean对象 二:ApplicationContext的实现类 1:ClassPathXmlApplicationContext 它是从类的根路径下…

【无标题】idea的lombok插件支持@SuperBuilder注解啦

在我的博客阅读本文 1. 前言 今早进公司打开idea,弹出更新提示,简单看了下,原来是idea的lombok插件更新了,惊喜的发现update log上写着Add support for SuperBuilder。 为什么说是惊喜呢?因为之前也有用到这个的场景…

DIY 云成本优化的5大挑战

外星人。 黑暗骑士。 帝国反击战。 在 Incredibuild,如果有一样东西我们喜欢,那就是我们的话题永远有续集。我们将针对云端优化推出一个更深入的续集, 但这一次我们谈论的是云成本优化,采用 DIY 的方式。(如果你还不…

Kubernetes Gateway API V1.0:您应该切换吗?

自Kubernetes Gateway API 发布 v1.0以来已经过去两个多月了,这标志着其一些关键 API 已经进入普遍可用状态。 去年,当网关 API升级为测试版时,我曾写过有关该 API的文章,但一年后,问题仍然存在。您是否应该从 Ingres…

SV-DJS-I13 深圳锐科达电梯 IP 五方对讲规格书

SV-DJS-I13 深圳锐科达电梯 IP 五方对讲规格书 DJS-I13 是专门对行业用户需求研发的一款 SIP 电梯五方对讲。它不仅有稳定性 好、电信级音质的优点,且完美兼容当下所有基于 SIP 的主流 IPPBX/软交换/IMS 平台, 如 Asterisk, Broadsoft, 3CX, Elastix 等。它集…

【MySQL用户管理】

目录: 前言用户管理创建用户删除用户修改用户密码修改用户密码安全检测设置 用户权限添加权限回收权限 总结 前言 剑指offer:一年又13天 用户管理 mysql> show databases; -------------------- | Database | -------------------- | inf…

[Python进阶] Python处理txt文件:open

7.1 Python处理txt文件:open 在Python中,通过open函数可以打开一个文件,创建一个file对象,然后对该对象进行读写。 函数语法: open(name[, mode[, buffering]…]) 参数说明: name:文件名 mode&…

你真的知道2024程序员搞钱新姿势吗?

2023年已经过去,2024的序曲已经奏响!回顾2023,我们经历了降薪裁员的大趋势,身为程序员也有点惶惶不可终日,害怕会失去工作,害怕面对家人无奈的模样,害怕跟不上时代的步伐,沦为被大环…

Minitab 各版本安装指南

Minitab下载链接 https://pan.baidu.com/s/1PLqocknkoRGGI9lbV3e45A?pwd0531 1.鼠标右击【Minitab 21(64bit)】压缩包(win11及以上系统需先点击“显示更多选项”)选择【解压到 Minitab 21(64bit)】。 2.打开解压后的文件夹,鼠标右击【setu…

众和策略安全可靠:如何区分大盘股和小盘股?大盘股指数有哪些?

股票根据流转盘能够划分为大盘股、中盘股、小盘股这三大类,那么,怎么区别大盘股和小盘股?大盘股指数有哪些?下面众和策略为我们准备了相关内容,以供参考。 怎么区别大盘股和小盘股? 根据个股的市值来区别…

使用异构图学习破解推荐系统 - 第 1 部分

Lokesh Sharma – Medium 一、说明 所以,这是独家新闻:异质图拥有一个充满潜力的世界,而常规图却无法做到这一点。传统的同构图很难处理不同关系和边类型的复杂性。现在是大炮的时候了——先进的架构可以解决具有多种边缘和关系类型的数据集的…

Go后端开发 -- Go Modules

Go后端开发 – Go Modules 文章目录 Go后端开发 -- Go Modules一、什么是Go Modules?二、GOPATH的工作模式1.GOPATH模式2.GOPATH模式的弊端 三、Go Modules模式创建项目1.go mod命令2.go mod环境变量3.使用Go Modules初始化项目4.修改模块的版本依赖关系 四、Go Modules下impo…

快速实现前后端表单交互(minui)

(1)使用minui快速生成表单 (2)修改样式,将生成的html文件发送给后端 (3)前后端交互(后端如何处理不太清楚) 表单初始化:新增修改(包括查看&…

如何远程修改plc程序?

​在工业自动化领域中,PLC(可编程逻辑控制器)是不可或缺的一部分。然而,传统方式下修改PLC程序需要亲临现场,这不仅耗时,还增加了成本。那么有没有一种更加高效、便捷的方法呢?答案是肯定的&…

【REST2SQL】03 GO读取JSON文件

REST2SQL需要一些配置信息,用JSON文件保存,比如config.json 1 创建config.json配置文件 {"hostPort":"localhost:5217","connString":"oracle://blma:5217127.0.0.1:1521/CQYH","_oracle":"ora…

6591A便携式光伏组串测试仪

01 6591A便携式光伏组串测试仪 产品综述: 6591A主要用于1500V光伏组串的测试,能够方便、快速的测试1500V光伏组串在自然光照下的工作特性,可为太阳能电站设计、验收、维护提供测试保障,是电站建设单位、质检部门、生产厂家、科研…

Navicat 技术干货 | 聚合查询的介绍

基础 SQL 查询可以检索、插入、更新和删除记录,而聚合查询可通过提供求和、平均值或最大/最小值等的大型结果集,将数据库交互提升到一个新的水平。本文中,我们将探索聚合 SQL 查询的基础知识,并研究如何有效的利用他们来分析和汇总…

边坡安全监测预警系统——高效率

安装边坡安全监测预警系统的原因是多方面的,涉及到社会效益、经济效益和环境效益。随着国家基础设施建设的快速发展,边坡安全监测预警系统的需求越来越迫切。 边坡安全监测预警系统对于保障人民生命财产安全具有重要意义。在山区、丘陵地带,边…

牛客网面试题知识点记录-03

1.题目讲解重写后子类调用父类的方法总结:当子类重写了父类方法A,父类方法直接调用被重写的父类方法后,调用的是子类的重写的父类方法A。 class Test {public static void main(String[] args) {System.out.println(new B().getValue());}st…

安科瑞电化学储能电能管理系统解决方案——安科瑞 顾烊宇

1 概述 在我国新型电力系统中,新能源装机容量逐年提高,但是新能源比如光伏发电、风力发电是不稳定的能源,所以要维持电网稳定,促进新能源发电的消纳,储能将成为至关重要的一环,是分布式光伏、风电等新能源消…