Synchronized底层实现,锁升级的具体过程

面试官:说一下Synchronized底层实现,锁升级的具体过程? 介绍 这是我去年7,8月份面试的时候被问的一个面试题,说实话被问到这个问题还是很意外的,感觉这个东西没啥用啊,直到后面被问了一波new Object,Integer对象等作为加锁对象行吗?会出现哪些问题?为啥java6后synchronized性能大幅上升?我彻底蒙蔽了。下面详细总结一下 synchronized使用方式 我们知道并发编程会产生各种问题的源头是可见性,原子性,有序性。 而synchronized能同时保证可见性,原子性,有序性。所以我们在解决并发问题的时候经常用synchronized,当. 阅读详情

锁的各种概念

  1. 自旋锁:是指当一个线程在获取锁的时候,如果锁已经被其他线程获取,那么该线程将循环等待,然后不断的判断锁是否能够成功获取,直到获取到锁才会退出循环。

  2. 乐观锁:假定没有冲突,在修改数据时如果发现数据和之前获取的不一致,则读最新数据,重试修改。

  3. 悲观锁:假定会发生并发冲突,同步所有对数据的相关操作,从读数据就开始上锁。

  4. 独享锁(写):给资源加上写锁,线程可以修改资源,其他线程不能再加锁;(单写)

  5. 共享锁(读):给资源加上读锁后只能读不能修改,其他线程也只能加读锁,不能加写锁;(多读)

  6. 可重入锁、不可重入锁:线程拿到一把锁之后,可以自由进入同一把锁所同步的其他代码。

  7. 公平锁、非公平锁:争抢锁的顺序,如果是按先来的先抢到,则为公平。

synchronized

为何jdk1.6后synchronized效率高

在Java早期版本中,synchronized属于重量级锁,效率低下,因为操作系统实现线程之间的切换时需要从用户态转换到核心态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高。

庆幸的是在jdk1.6之后Java官方对从JVM层面对synchronized较大优化,所以现在的synchronized锁效率也优化得很不错了,Jdk1.6之后,为了减少获得锁和释放锁所带来的性能消耗,引入了偏向锁和轻量级锁。

特性

可重入、独占、悲观

锁消除

锁消除是发生在编译器级别的一种锁优化方式。有时候我们写的代码完全不需要加锁,却执行了加锁操作。比如,StringBuffer类的append操作:

@Override
public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}

从源码中可以看出,append方法用了synchronized关键词,它是线程安全的。但我们可能仅在线程内部把StringBuffer当作局部变量使用:

public class Demo {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        int size = 10000;
        for (int i = 0; i < size; i++) {
            createStringBuffer("Hyes", "为分享技术而生");
        }
        long timeCost = System.currentTimeMillis() - start;
        System.out.println("createStringBuffer:" + timeCost + " ms");
    }
    public static String createStringBuffer(String str1, String str2) {
        StringBuffer sBuf = new StringBuffer();
        sBuf.append(str1);// append方法是同步操作
        sBuf.append(str2);
        return sBuf.toString();
    }

代码中createStringBuffer方法中的局部对象sBuf,就只在该方法内的作用域有效,每次调用createStringBuffer()方法时,都会创建不同的sBuf对象,因此此时的append操作若是使用同步操作,就是白白浪费的系统资源。这时我们可以通过编译器将其优化,将锁消除,前提是java必须运行在server模式(server模式会比client模式作更多的优化),同时必须开启逃逸分析:-server -XX:+DoEscapeAnalysis -XX:+EliminateLocks 其中+DoEscapeAnalysis表示开启逃逸分析,+EliminateLocks表示锁消除。

锁粗化

通常情况下,为了保证多线程间的有效并发,会要求每个线程持有锁的时间尽可能短,但是大某些情况下,一个程序对同一个锁不间断、高频地请求、同步与释放,会消耗掉一定的系统资源,因为锁的讲求、同步与释放本身会带来性能损耗,这样高频的锁请求就反而不利于系统性能的优化了,虽然单次同步操作的时间可能很短。锁粗化就是告诉我们任何事情都有个度,有些情况下我们反而希望把很多次锁的请求合并成一个请求,以降低短时间内大量锁请求、同步、释放带来的性能损耗。

一种极端的情况如下:

public void doSomethingMethod(){
    synchronized(lock){
        //do some thing
    }
    //这是还有一些代码,做其它不需要同步的工作,但能很快执行完毕
    synchronized(lock){
        //do other thing
    }
}

上面的代码是有两块需要同步操作的,但在这两块需要同步操作的代码之间,需要做一些其它的工作,而这些工作只会花费很少的时间,那么我们就可以把这些工作代码放入锁内,将两个同步代码块合并成一个,以降低多次锁请求、同步、释放带来的系统性能消耗,合并后的代码如下:

public void doSomethingMethod(){
    //进行锁粗化:整合成一次锁请求、同步、释放
    synchronized(lock){
        //do some thing
        //做其它不需要同步但能很快执行完的工作
        //do other thing
    }
}

另一种需要锁粗化的极端的情况是:

for(int i=0;i<size;i++){
    synchronized(lock){
    }
}

上面代码每次循环都会进行锁的请求、同步与释放,看起来貌似没什么问题,且在jdk内部会对这类代码锁的请求做一些优化,但是还不如把加锁代码写在循环体的外面,这样一次锁的请求就可以达到我们的要求,除非有特殊的需要:循环需要花很长时间,但其它线程等不起,要给它们执行的机会。

锁粗化后的代码如下:

synchronized(lock){ for(int i=0;i<size;i++){ } }

Java 对象在内存中的布局

对象和他的属性存储在堆中,对象中的String和其他对象类型字段通过引用方式,比如对象Teachaer中student字段指向Student实例,每个对象通过对象头指向所属类型

对象头

如上图所示,对象头分为三部分:

  1. Mark Word :对象加锁(synchronized(this))状态信息记录在Mark Word中,包括右侧锁相关信息,State代表锁所处的状态,如State值 Unlocked未锁定状态、Biased/biasable偏向锁、Leavy-weight locked重量级锁,Light-weight locked 轻量级锁

  2. Class Meta address 指向方法区的类对象,表明当前对象的类信息

  3. Array lengh 对象中数组字段的长度,有的对象有数组字段。

锁升级过程 -不同状态

  1. 几种锁的区别

    在这里插入图片描述

  2. 偏向锁因为经过HotSpot的作者大量的研究发现,大多数时候是不存在锁竞争的,常常是一个线程多次获得同一个锁,因此如果每次都要竞争锁会增大很多没有必要付出的代价,为了降低获取锁的代价,才引入的偏向锁。偏向锁的目标是,减少无竞争且只有一个线程使用锁的情况下,使用轻量级锁产生的性能消耗。轻量级锁每次申请、释放锁都至少需要一次CAS,但偏向锁只有初始化时需要一次CAS。

    偏向锁原理和升级过程

    当线程1访问代码块并获取锁对象时,会在java对象头和栈帧中记录偏向的锁的threadID,因为偏向锁不会主动释放锁,因此以后线程1再次获取锁的时候,需要比较当前线程的threadID和Java对象头中的threadID是否一致,如果一致(还是线程1获取锁对象),则无需使用CAS来加锁、解锁;如果不一致(其他线程,如线程2要竞争锁对象,而偏向锁不会主动释放因此还是存储的线程1的threadID),那么需要查看Java对象头中记录的线程1是否存活,如果没有存活,那么锁对象被重置为无锁状态,其它线程(线程2)可以竞争将其设置为偏向锁;如果存活,那么立刻查找该线程(线程1)的栈帧信息,如果还是需要继续持有这个锁对象,那么暂停当前线程1,撤销偏向锁,升级为轻量级锁,如果线程1 不再使用该锁对象,那么将锁对象状态设为无锁状态,重新偏向新的线程。

    3.轻量级锁

    为什么要引入轻量级锁?

    轻量级锁考虑的是竞争锁对象的线程不多,而且线程持有锁的时间也不长的情景。因为阻塞线程需要CPU从用户态转到内核态,代价较大,如果刚刚阻塞不久这个锁就被释放了,那这个代价就有点得不偿失了,因此这个时候就干脆不阻塞这个线程,让它自旋这等待锁释放。

    如上图所示:两个或多个线程在方法栈中开辟一个空间命名为 Lock Record,将锁对象头的信息(Hashcode Age 0 01,未锁定状态值,并非偏向锁)拷贝到Lock Record,然后通过CAS进行争抢锁。

    当某一线程争抢成功,则锁对象头的mark word值变为 Lock Record Adress 0 0,mark word指向持有锁线程的栈空间Lock Record地址,持有锁的线程方法栈会有一个ower变量指向锁的对象头,表明当前的锁的持有者。

    争抢锁失败的线程进入自旋,因为自旋是要消耗CPU的,因此自旋的次数是有限制的,比如10次或者100次,如果自旋次数到了线程1还没有释放锁,或者线程1还在执行,线程2还在自旋等待,这时又有一个线程3过来竞争这个锁对象,那么这个时候轻量级锁就会膨胀为重量级锁。重量级锁把除了拥有锁的线程都阻塞,防止CPU空转。

    4.重量级锁

    紧接着上面轻量级锁步骤,重量级锁对象头值变为Monitor adress 1 0。此时对象的Monitor(应该升级到重量级锁才有的,具体不进行讨论)监视器含有三个要属性:entryList(锁池,抢锁未成功的锁进入)、WaitSet(等待池,调用wait方法进入)、owner(指向线程持有者)

    轻量级锁中未争抢到锁的线程(t2),自悬到一定次数,进入到entryList,ower属性的值是持有锁的线程值(t1),此时如果有其他线程(t3)来抢锁,因为锁被t1持有,(t3根据ower不为空),直接进入等待队列entryList;

    如果持有锁的线程(t1)执行了wait()则释放锁进入WaitSet中,此时owner为null,entryList中的线程被唤醒进行争抢锁,entryList线程出栈规则是先进先出,t2先出栈进行抢锁,此时如果有其他新的线程(t4)进入抢锁,则有可能新进入的线程(t4)抢锁成功,这就说明synronized是非公平的锁.

    当t1被唤醒(其他线程唤醒或者wait到一定时间自己唤醒),此时线程已经被其他线程持有,会进入锁池entryList阻塞排队。

    持有锁的线程,执行完毕,会调用monitorExit指令结束。

Xilinx ERNIC RoCEv2 FPGA实现与QP队列深度解析 本文深入解析Xilinx ERNIC RoCEv2在FPGA上的实现,重点剖析QP队列对的管理机制与深度优化策略。通过Verilog代码示例和工程实战,详细介绍了如何实现高吞吐量、低延迟的RDMA硬件卸载方案,为金融交易和数据中心应用提供性能优化指导。 阅读详情

相关推荐

nRF开发环境搭建全攻略:VS Code + nRF Connect SDK + Zephyr实战

嵌入式开发中,环境配置往往是新手入门的第一道障碍,尤其面对基于Zephyr RTOS的Nordic nRF平台时,工具链的复杂度常让人望而却步。理解构建系统、SDK版本管理和调试工具之间的协作关系,是高效开发的前提。Zephyr作为开源实时操作系统,通过设备树和Kconfig实现硬件描述与功能配置,而nRF Connect SDK(NCS)则是Nordic基于Zephyr的增强发行版,两者结合为物联网开发提供了强大支持。基于VS Code搭建开发环境,配合west工具管理多仓库、CMake/Ninja执行构

weixin_29188997的博客 228

Synchronized锁的升级过程

synchronized锁升级过程依次为无锁、偏向锁、轻量级锁、重量级锁,部分文章认为synchronized锁不存在降级过程,但在openjdk的hotsopt jdk8u里是有锁降级的机制的。 对象头 锁升级示意图 1. 无锁到偏向锁 线程A执行到同步代码块时,检查对象头锁标志位是否为01,再看偏向锁标志位是否为0(即检查对象是否为无锁状态),通过CAS操作尝试修改MarkWord字段,这里CAS操作只尝试一次,失败的话说明发生锁竞争,立即升级为轻量级锁。成功修改后执行同步代码块,执行完毕后不主动释

举个锤子的博客 4741

vue - 网站接入微信扫码登录功能详细教程,完整详细流程及完整功能示例源代码(附常见问题的解决方案与二维码样式修改方案)

vue.js项目植入微信登录,vue网页项目怎么做微信扫码登录,VUE项目中的微信扫码登录,VUE中最简单的js调用微信扫一扫功能,vue:如何设计网页的微信扫码登录功能,微信扫码功能官方文档,通过vue方式实现二维码扫码功能,vue调用微信扫码,vue H5 调用微信扫描二维码,Vue前端实现微信扫码登录,vue-wxlogin 微信授权登录组件,网站引入的微信登录框如何修改宽高或其他样式,wxlogin 组件插件怎么改变宽高,vue-wxlogin组件怎么自定义样式,微信网页授权,vue调用微信JSDK

✅ 精选推荐文章 6367

synchronized锁升级过程

synchronized锁升级以及锁优化的各种方式

m0_51808048的博客 604

GEE训练教程:如何批量从Google assets中将时序影像批量导出到Google drive

本文介绍了一种使用Google Earth Engine(GEE)批量导出2000-2023年叶绿素a(Chl-a)浓度数据的方法。通过JavaScript代码自动生成多个年份的导出任务,将数据保存至Google Drive指定文件夹。文章详细说明了代码参数配置、使用步骤和注意事项,包括资产命名规则验证、导出任务启动方式,以及分辨率、区域范围等关键参数的调整建议。同时提供了常见问题的解决方案,如处理缺失年份数据、解决导出像素超限等问题。该方法可显著提高多年份Chl-a数据批量导出的效率,适用于水体环境研究的

此星光明博客 91

Qt EGLFS 图形后端详解

EGLFS (EGL Full Screen) 是Qt框架在嵌入式Linux系统中的高性能图形显示后端,它提供了直接基于EGL和OpenGL ES的硬件加速渲染解决方案。

浩瀚之水的专栏 513

synchronized底层实现原理及锁升级过程

文章目录一 前言二 底层实现原理三 锁升级过程3.1 对象内存结构3.2 锁升级过程四 总结 一 前言 synchronized是JDK自带的一个关键字,用于在多线程的情况下,保证线程安全;在JDK1.5之前是一个重量级锁,1.6之后进行了优化,性能有很大提升。synchronized可以用来同步方法、同步代码块、同步静态方法,具体用法可参见《Java多线程(三)——synchronized》,本文主要研究synchronized底层实现原理及锁升级过程。 二 底层实现原理 public class Sy

pandamig的博客 1422

Scale-up,scale-out技术概述

本文系统分析了Scale-up与Scale-out两种扩展技术在现代计算中的应用与发展。Scale-up通过增强单节点性能,适用于高耦合任务,而Scale-out通过横向扩展节点,实现高可用性和无限扩展。在芯片层面,Chiplet技术推动Scale-up发展;在数据中心层面,二者协同工作构建强大集群。光模块在长距离高带宽传输中发挥关键作用。文章对比了NVIDIA、AMD和Broadcom三家公司的技术路线,指出NVIDIA采用封闭生态,AMD选择开放标准,Broadcom则主导底层连接技术。未来发展方向将聚

元直的博客 1447

Sychronized锁升级的详细分析

本文通过偏向锁、轻量级锁、重量级锁几种锁的基本概念解释和代码demo运行,简单给大家分享了对应的锁概念,并对sychronized的锁升级过程进行了解释,希望能帮助到大家对锁的理解,后期会出更详细的锁文章。

橙子的随笔集 665

OpenCASCADE完整教程专栏完整目录

OpenCASCADE完整教程专栏完整目录

希望我的博客,能帮上你解决学习中工作中所遇到的问题 9706

10、智慧变电站 - 加载vue大屏元素(整体效果)

这一篇文章非常简单,但是也非常出效果阅读前面几篇文章,都可以看到有顶部、左侧、右侧的面板,主要我不会录gif图片,大屏元素面板还有一些动态加载的效果。 vue的transition标签 Vue 提供了 transition 的封装组件,可以给任何元素和组件添加进入/离开过渡;在进入/离开的过渡中,会有 6 个 class 切换:v-enter 、v-enter-active 、v-enter-to 、v-leave 、v-leave-active 、v-leave-to。我们正好使用这个特性来达到面板进入

github_39988036的博客 1041

synchronized锁升级_58面试官居然问我Synchronized底层实现,锁升级的具体过程?

介绍这是我去年7,8月份面58被问的一个面试题,说实话被问到这个问题还是很意外的,感觉这个东西没啥用啊,直到后面被问了一波new Object,Integer对象等作为加锁对象行吗?会出现哪些问题?为啥java6后synchronized性能大幅上升?我彻底蒙蔽了。下面详细总结一下synchronized使用方式我们知道并发编程会产生各种问题的源头是可见性,原子性,有序性。而synchr...

weixin_39683858的博客 77

影楼精修-AI追色算法解析

本文解析了像素蛋糕软件中AI追色功能的技术原理与实现方案,为理解AI追色技术提供了实用参考。

Trent1985的专栏 2万+

面试-说一下Synchronized底层实现,锁升级的具体过程?

面试官:说一下Synchronized底层实现,锁升级的具体过程? https://blog.youkuaiyun.com/zzti_erlie/article/details/103997713

Eistert 221

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

java锁上升_详解Synchronized底层实现,锁升级的具体过程,与Lock的区别

介绍这是我去年7,8月份面58被问的一个面试题,说实话被问到这个问题还是很意外的,感觉这个东西没啥用啊,直到后面被问了一波new Object,Integer对象等作为加锁对象行吗?会出现哪些问题?为啥java6后synchronized性能大幅上升?我彻底蒙蔽了。下面详细总结一下synchronized使用方式我们知道并发编程会产生各种问题的源头是可见性,原子性,有序性。而synchronize...

weixin_32365777的博客 310

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

无法获取 dpkg 前端锁_详解Synchronized底层实现,锁升级的具体过程,与Lock的区别...

介绍这是我去年7,8月份面58被问的一个面试题,说实话被问到这个问题还是很意外的,感觉这个东西没啥用啊,直到后面被问了一波new Object,Integer对象等作为加锁对象行吗?会出现哪些问题?为啥java6后synchronized性能大幅上升?我彻底蒙蔽了。下面详细总结一下synchronized使用方式我们知道并发编程会产生各种问题的源头是可见性,原子性,有序性。而synchronize...

weixin_39743824的博客 165

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

synchronized同步锁的特性和底层实现

synchronized锁的是对象 不是锁的代码 作用在方法上时相当于synchronized(this),即锁的是当前对象本身。 如果修饰的是静态方法,那就是类对象锁了。 synchronized获得的锁是可重入的 一个线程已经拥有某个对象的锁,再次申请的时候仍然会得到该对象的锁。一个同步方法里面可以调用另外一个同步方法(前提是锁对象一样),否则会发生死锁。 如下例子 //如果m1执行时,调用m2还需要再获得锁,但锁对象它已占用,所以直接可以调用,避免了死锁发生 public class T {

csdn_xxm的博客 451

synchronized 底层原理总结

文章目录一.synchronized介绍二.synchronized使用方式三.java对象组成四.锁的升级过程 本文总结下Synchronized关键字的底层实现原理。 一.synchronized介绍 synchronized是JVM内置锁,通过内部对象Monitor(监视器锁)来实现,基于进入与退出monitor对象来实现方法与代码块的同步,监视器锁的实现,最终依赖操作系统的Mutex lo...

雨剑的博客 461

synchronized 剖析

重入计数 = 0 → 将 Lock Record 中原始 Mark Word 写回对象头,锁真正释放。+-----------------+ | 重入计数 = 1 |2. CAS 将对象 O 的 Mark Word 指向 Lock Record → 锁被 T1 占用。线程尝试使用 CAS 将对象头的 Mark Word 指向栈上锁记录(Lock Record)- CAS 将对象头 Mark Word 指向 Lock Record → 轻量级锁。

zsjdhr的博客 559

JUC学习笔记(二) synchronized 底层原理探究

synchronized 底层原理探究,锁膨胀机制,对象存储结构,监视器对象结构,加锁和解锁流程,可重入性

slxysyka的博客 422

synchronized底层实现原理及锁优化

一、概述 1、synchronized作用 原子性:synchronized保证语句块内操作是原子的 可见性:synchronized保证可见性(通过“在执行unlock之前,必须先把此变量同步回主内存”实现) 有序性:synchronized保证有序性(通过“一个变量在同一时刻只允许一条线程对其进行lock操作”) 2、synchronized的使用 修饰实例方法,对当前实例对象加锁 修饰...

Medlen 7万+

深入理解synchronized锁升级过程

1.简介 在Java高并发系统中,我们常常需要使用多线程技术来提高系统的运行速度,而多线程带来的数据安全问题就是我们必须要解决的问题。在Java中,可以使用synchronized关键字来实现多线程并发中的数据安全问题。 这里简单介绍下synchronized的三种用法: 修饰实例方法:以实例对象作为锁,进入同步代码前需要获得当前实例对象的锁 修饰类方法(static修饰的方法):以类对象为锁,进入同步代码块前需要获得当前类对象的锁 修饰代码块:需要指定一个锁对象(既可以是实例对象,也可以是) 即

weixin_40910372的博客 1万+

synchronized加锁原理以及锁升级过程

由于synchronized中用到了CAS技术,此处先对CAS做个简单介绍。是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败。CAS(V,A,B)操作中至少包含三个操作数——需要读写的内存位置(V)、进行比较的预期原值(A)和拟写入的新值(B)。如果内存位置V的值与预期原值A相匹配,那么处理器会自动将该位置值更新为新值B。否则处理器不做任何操作。无论哪种情况,它都会在CAS指令之前返回该位置的值。在。

cxbtdd的博客 1787
上一篇: 请求合并
下一篇: 使用condition来实现阻塞队列
一切还来得及
博客等级 码龄11年 50粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值