synchronized关键字的底层原理

1synchronized关键字的底层原理

Monitor

举个例子:

1.线程1执行synchronized代码块,里面用到了lock(对象锁)。首先会让这个lock对象和monitor关联,判断monitor中的owner属性是否为null。如果为null直接获取对象锁。owner只能关联一个线程。

2.现在其他线程来了,发现owner不为空,全部进入entrylist中等待。一旦线程1执行完,把锁释放了,owner又为空了,这时候entrylist中的线程就会去抢对象锁(大家共同去抢,不是先来先得!)。

3.当线程进入synchronized同步代码块并调用wait()方法时,它会释放对该对象的锁,并将自身放入与该对象关联的monitor对象的waitset中。这样,线程就进入了等待状态,等待其他线程调用notify()或notifyAll()方法将其唤醒。

2.synchronized关键字的底层原理-进阶

1.锁升级

注:一旦发生线程竞争的情况还是要用到monitor。

2.对象锁如何关联到monitor的?
  1. 对象头:这部分包含了对象的元数据信息,如对象的哈希码、分代年龄、锁标志位等。对于非数组对象,对象头通常是8字节,而对于数组对象,对象头会增加4字节用于存储数组的长度。
  2. 实例数据 :这部分包含了对象的非静态成员变量。这部分的大小取决于对象中属性的数量和类型。例如,一个int类型的属性会占用4字节,一个String类型的属性会占用更多的字节。
  3. 对齐填充:这部分是为了确保对象的总大小是8的倍数。这是因为HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是对象的大小必须是8字节的整数倍。

如果使用synchronized给对象上锁(重量级)之后,对象锁的MarkWord(对象头)中的ptr_to_heavyweight_monitor就被设置指向monitor对象的地址。由此来找到需要关联的monitor。

3.轻量级锁
java 复制代码
    static final Object obj = new Object();
    public static void method1() {
        synchronized (obj) {
            // 同步块 A
            method2();//调用method2
        }
    }
    public static void method2() {
        synchronized (obj) {//同一线程重入同一把锁
        // 同步块 B
        }
    }

加锁过程

1.在线程栈中创建一个Lock Record,将其obj字段指向锁对象。

2.通过CAS指令将Lock Record的地址存储在对象头的mark word中(数据进行交 换),如果对象处于无锁状态则修改成功,代表该线程获得了轻量级锁。

3.如果是当前线程已经持有该锁了,代表这是一次锁重入。设置Lock Record第一 部分为null,起到了一个重入计数器的作用。

4.如果CAS修改失败,说明发生了竞争,需要膨胀为重量级锁。

解锁过程

1.遍历线程栈,找到所有obj字段等于当前锁对象的Lock Record。

2.如果Lock Record的Mark Word为null,代表这是一次重入,将obj设置为null后 continue。

3.如果Lock Record的 Mark Word不为null,则利用CAS指令(保证原子性)将对象头的mark word 恢复成为无锁状态。如果失败则膨胀为重量级锁。

4.偏向锁(轻量级锁的优化)

轻量级锁在没有竞争时(就自己这个线程),每次重入仍然需要执行 CAS 操作。

Java 6 中引入了偏向锁来做进一步优化:只有第一次使用 CAS 将线程 ID 设置到对象的 Mark Word 头,之后发现:这个线程 ID 是自己的就表示没有竞争,不用重新 CAS。以后只要不发生竞争, 这个对象就归该线程所有。

java 复制代码
    static final Object obj = new Object();
    public static void m1() {
        synchronized (obj) {//第一次获取锁
        // 同步块 A
            m2();
        }
    }
    public static void m2() {
        synchronized (obj) {//第一次重入锁
        // 同步块 B
            m3();
        }
    }
    public static void m3() {
        synchronized (obj) {//第二次重入锁

        }
    }

多次重入锁,可以用偏向锁提升性能。

加锁的流程

1.在线程栈中创建一个Lock Record,将其obj字段指向锁对象。

2.通过CAS指令将Lock Record的线程id存储在对象头的mark word中,同时也设 置偏向锁的标识为101,如果对象处于无锁状态则修改成功,代表该线程获得了 偏向锁。

3.如果是当前线程已经持有该锁了,代表这是一次锁重入。设置Lock Record第一 部分为null,起到了一个重入计数器的作用。与轻量级锁不同的时,这里不会再 次进行cas操作,只是判断对象头中的线程id是否是自己,因为缺少了cas操作, 性能相对轻量级锁更好一些。

解锁流程参考轻量级锁

偏向锁只会在第一次添加锁的时候进行CAS操作,之后每一次重入只是判断当前锁是不是当前线程的。而轻量级锁每次重入都会执行一次CAS操作。因此偏向锁性能更好一点。

5.小结
相关推荐
在努力的前端小白2 小时前
Spring Boot 敏感词过滤组件实现:基于DFA算法的高效敏感词检测与替换
java·数据库·spring boot·文本处理·敏感词过滤·dfa算法·组件开发
完全学不完2 小时前
JVM对象创建和内存分配
jvm
Coovally AI模型快速验证4 小时前
农田扫描提速37%!基于检测置信度的无人机“智能抽查”路径规划,Coovally一键加速模型落地
深度学习·算法·yolo·计算机视觉·transformer·无人机
pusue_the_sun4 小时前
数据结构:二叉树oj练习
c语言·数据结构·算法·二叉树
一叶飘零_sweeeet4 小时前
从繁琐到优雅:Java Lambda 表达式全解析与实战指南
java·lambda·java8
RaymondZhao345 小时前
【全面推导】策略梯度算法:公式、偏差方差与进化
人工智能·深度学习·算法·机器学习·chatgpt
艾伦~耶格尔5 小时前
【集合框架LinkedList底层添加元素机制】
java·开发语言·学习·面试
zhangfeng11335 小时前
DBSCAN算法详解和参数优化,基于密度的空间聚类算法,特别擅长处理不规则形状的聚类和噪声数据
算法·机器学习·聚类
一只叫煤球的猫5 小时前
🕰 一个案例带你彻底搞懂延迟双删
java·后端·面试
最初的↘那颗心5 小时前
Flink Stream API 源码走读 - print()
java·大数据·hadoop·flink·实时计算