码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 【并发编程】Synchronized原理详解


    📫作者简介:小明java问道之路,2022年度博客之星全国TOP3,专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化,文章内容兼具广度、深度、大厂技术方案,对待技术喜欢推理加验证,就职于知名金融公司后端高级工程师。

            

    📫 热衷分享,喜欢原创~ 关注我会给你带来一些不一样的认知和成长。

            

    🏆 2022博客之星TOP3 | CSDN博客专家 | 后端领域优质创作者 | CSDN内容合伙人

    🏆 InfoQ(极客邦)签约作者、阿里云专家 | 签约博主、51CTO专家 | TOP红人、华为云享专家

            

    🔥如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主~ 


    🍅 文末获取联系 🍅  👇🏻 精彩专栏推荐订阅收藏 👇🏻

    专栏系列(点击解锁)

    学习路线(点击解锁)

    知识定位

    🔥Redis从入门到精通与实战🔥

    Redis从入门到精通与实战

    围绕原理源码讲解Redis面试知识点与实战

    🔥MySQL从入门到精通🔥

    MySQL从入门到精通

    全面讲解MySQL知识与企业级MySQL实战

    🔥计算机底层原理🔥

    深入理解计算机系统CSAPP

    以深入理解计算机系统为基石,构件计算机体系和计算机思维

    Linux内核源码解析

    围绕Linux内核讲解计算机底层原理与并发

    🔥数据结构与企业题库精讲🔥

    数据结构与企业题库精讲

    结合工作经验深入浅出,适合各层次,笔试面试算法题精讲

    🔥互联网架构分析与实战🔥

    企业系统架构分析实践与落地

    行业最前沿视角,专注于技术架构升级路线、架构实践

    互联网企业防资损实践

    互联网金融公司的防资损方法论、代码与实践

    🔥Java全栈白宝书🔥

    精通Java8与函数式编程

    本专栏以实战为基础,逐步深入Java8以及未来的编程模式

    深入理解JVM

    详细介绍内存区域、字节码、方法底层,类加载和GC等知识

    深入理解高并发编程

    深入Liunx内核、汇编、C++全方位理解并发编程

    Spring源码分析

    Spring核心七IOC/AOP等源码分析

    MyBatis源码分析

    MyBatis核心源码分析

    Java核心技术

    只讲Java核心技术

    本文目录

    本文导读

    一、synchronized 的底层原理

    二、对象头在JVM中存储的形式

    三、synchronized锁的优化(锁升级)

    1、偏向锁、轻量级锁、重量级锁的升级过程

    2、偏向锁、轻量级锁、重量级锁的应用场景

    总结


    本文导读

    Synchronized用的锁是存在java的对象头里面的。一个对象被new出来之后包含四个部分:对象头、类型指针、实例数据、对齐填充。

    对象头 Mark Word 存储了对象的 hashCode、GC信息、锁信息三部分,这部分占8字节。每个对象都有个 monitor 对象,加锁就是在竞争 monitor 对象,代码块加锁是在前后分别加上 monitorenter 和 monitorexit 指令来实现的,方法加锁是通过一个标记位来判断的。

    一、synchronized 的底层原理

    synchronized是一种对象锁,是可重入的,但是不可中断的(这个不可中断指的是在阻塞队列中排队是不可中断),非公平的锁。

    加了synchronized后,在字节码会有二个指令monitorenter、monitorexit。

    每个对象都是一个监视器锁(monitor),当monitor被占用时就会处于锁定状态,线程执行 monitorenter指令时尝试获取monitor的所有权,如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor 的所有者;如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1;如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝 试获取monitor的所有权。

    执行monitorexit的线程必须是objectref所对应的monitor的所有者,指令执行时,monitor的进入数减 1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去 获取这个 monitor 的所有权。

    Monitor 对象存在于每个 Java对象的对象头里(存储的指针的指向)。synchronized重量级锁是依赖对象内部的Monitor锁来实现的,而Monitor又依赖操作系统的MutexLock(互斥锁)来实现的,所以重量级锁也称为互斥锁。

    二、对象头在JVM中存储的形式

    Java对象头一般占有2个机器码(在32位虚拟机中,1个机器码等于4字节,也就是32bit, 在64位虚拟机中,1个机器码是8个字节,也就是64bit)

    对象头主要由如下两个部分组成:Mark Word:存储的是 对象的 hashCode、锁信息、或分代年龄、GC标注等信息。Class Metadata Address: 存储对象所属类(元数据) 的指针,JVM通过这个确定这个对象属于哪个类。

    实例数据:存放类的属性数据信息,包括父类的属性信息

    对齐填充:由于虚拟机要求 对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐。

    三、synchronized锁的优化(锁升级)

    Monitor实现的锁是互斥锁也称为重量级锁,重量级锁有一个缺点是线程开销很大。原因是当系统检测到锁是重量级锁后,会把等待的想要获得锁的线程进行阻塞,被阻塞的线程不会消耗CPU,但是阻塞或者唤醒一个线程时,都需要操作系统来帮忙,这就需要从用户态切换到内核态,而转换状态是需要消耗很多时间的,有可能比用户执行代码的时间还长。

    synchronized一共有四种锁状态,锁的级别由低到高分别是:无锁状态->偏向锁状态->轻量级锁状态->重量级锁状态。这几个状态会随着锁的竞争情况逐渐升级(只能升级,不能降级)。

    1、偏向锁、轻量级锁、重量级锁的升级过程

    偏向锁偏向锁不是一把锁,而是代表了当前synchronized 锁状态。 当只有一个来线程加锁的时候,此时synchronized 锁就会变成偏向锁,偏向锁代表这个锁偏向这个线程,就是说当这个线程再次来加锁的时候,不需要再向操作系统申请资源,而是很快就能获取到锁,减少了申请锁的开销。

    轻量级锁是当有多个线程参与到偏向锁的竞争中时,会先判断 markword 中的线程ID与这个线程是否一致,如果不一致,则会立即撤销偏向锁,升级为轻量级锁。

    轻量级锁在代码进入同步块前,如果该同步块没有被锁定(即锁的标志位为“01”),那么JVM在将当前线程的栈帧中,创建一个 LockRecord(锁记录LR),并将锁对象头中的 markWord 信息复制到锁记录(LR)中,这个官方称为 Displaced Mard Word,然后线程尝试使用 CAS 将对象头中的 MarkWord 替换为指向锁记录(LR)的指针。

    如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位(Mark Word的最后2bit)将转变为“00”,即表示此对象处于轻量级锁状态。

    如果失败,表示有其他线程竞争锁,当前线程便尝试使用自旋来获取锁。如果在自旋一定次数后仍为获得锁,那么轻量级锁将会升级成重量级锁。

    2、偏向锁、轻量级锁、重量级锁的应用场景

    总结

    synchronized用的锁是存在java的对象头里面的,加了synchronized后,在字节码会有二个指令monitorenter、monitorexit。

    Monitor 对象存在于每个 Java对象的对象头里(存储的指针的指向)。

    synchronized重量级锁是依赖对象内部的Monitor锁来实现的,而Monitor又依赖操作系统的MutexLock(互斥锁)来实现的。

    synchronized一共有四种锁状态,锁的级别由低到高分别是:无锁状态->偏向锁状态->轻量级锁状态->重量级锁状态。

  • 相关阅读:
    【权威出版/投稿优惠】2024年智慧城市与信息化教育国际会议(SCIE 2024)
    MySQL关联数据表操作方式
    return的用法是什么?若用在for循环中,还会执行下一次循环吗?
    LeetCode 1173.即时食物配送
    CloudQuery + StarRocks:打造高效、安全的数据库管控新模式
    springboot+mybatis实现一对一,一对多
    【ARM入门】ARM、SOC、ARM授权 概念篇
    UI线程和bitmap
    应用层Buffer缓冲区及其重要性
    C#堆排序算法
  • 原文地址:https://blog.csdn.net/FMC_WBL/article/details/134520463
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号