• Java 锁(synchronized)升级过程


    java中的锁是针对对象而言的,它锁住的是一个对象,并且具有可重入的性质。
    java中的对象内存结构如图所示
    普通对象内存结构:

    java普通对象结构
    数组对象内存结构:
    java数组对象
    其中关于锁状态的记录主要存储在_mark(markword)中,markword的结构如下以64位为例:
    markword
    最开始的synchronized的实现是直接启用重量级锁,这样对于效率的影响是比较大,在后来的改进中引入了锁升级的概念,来增加执行的效率

     public synchronized void testSync() {
            synchronized(this){
            System.out.println("test");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这个synchronized锁住的是包含当前方法的对象,当一个线程(Thread_1)来调用这个方法的时候,就可能会触发锁升级的过程,如下图所示:
    在这里插入图片描述
    自旋锁也叫轻量级锁,一般通过cas算法实现。
    注意:锁只会升级不会降级
    1、当Thread-1访问对象的时候,首先通过cas操作去获取偏向锁并将锁的偏向位更改为1;
    2、当另一个线程(thread-2)到达的时候会比较自身线程id和对象头中id是否一致,发现不一致就会去检测对象头中的线程是否存活,如果Thread-1还是存活的就升级为轻量级锁;
    3、如果获取失败则说明存在竞争关系,这时候将偏向锁升级为轻量级锁;升级为轻量级锁之后会在thread-2线程的栈帧中开辟一块锁记录空间叫做displaced Mark Word,并将锁对象的markword拷贝到线程本身的displaced Mark Word空间中,然后通过cas的方式去设置锁对像中线程id指针,并将锁的标志设置为00;
    4、当其中一个线程的自旋次数超过阈值(默认是10)的时候为了防止cpu空转,会将自旋锁升级为重量级锁,将对象监视器的指针存储在对象头之中。

    对象监视器结构如图
    // hotspot中的对象监视器
    ObjectMonitor() {
    _count = 0; //用来记录该对象被线程获取锁的次数
    _waiters = 0;
    _recursions = 0; //锁的重入次数
    _owner = NULL; //指向持有ObjectMonitor对象的线程
    _WaitSet = NULL; //处于wait状态的线程,会被加入到_WaitSet
    _WaitSetLock = 0 ;
    _EntryList = NULL ; //处于等待锁block状态的线程,会被加入到该列表
    }
    在升级为重量级锁之后会创建一个ObjectMonitor()对象,并在锁对象的markword中记录下objectMonitor的指针;
    在ObjectMonitor中有两个队列(entryList waitSet)和一个指针(owner),两个队列可以称为锁池和等待池,指针中记录的是当前拥有锁的线程;

  • 相关阅读:
    binary_cross_entropy和binary_cross_entropy_with_logits的区别
    TMUX终端复用工具小解
    关于实例变量、对象和引用
    OpenCV官方教程中文版 —— 图像梯度
    MySQL_Note8
    Linux 网络虚拟化 Macvlan(基于物理网络接口虚拟网络接口) 认知
    v-charts
    ROS2专题【02】:Ubuntu上安装ROS2
    分享从零开始学习网络设备配置--任务4.1 IPv6地址的基本配置
    AI绘画使用Stable Diffusion(SDXL)绘制三星堆风格的图片
  • 原文地址:https://blog.csdn.net/zjshuster/article/details/132806297