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.小结
相关推荐
梦@_@境17 分钟前
面向 Spring Boot 的可观测业务流程编排引擎
java·spring boot·后端
云烟成雨TD28 分钟前
Spring AI Alibaba 1.x 系列【77】执行取消
java·人工智能·spring
醇氧36 分钟前
【Linux】Java 服务生产级部署指南:实现常驻后台、开机自启与系统服务化管理
java·开发语言
JAVA面经实录9171 小时前
Netty 全套系统化学习文档(零基础到高阶面试完整版)
java·后端
菜鸟‍1 小时前
LeetCode 1 27 和 704 || 两数之和 移除元素 二分查找
算法·leetcode·职场和发展
weixin_523185321 小时前
Java面试高频题:Integer缓存机制与 equals、== 区别
java·缓存·面试
Hui Baby1 小时前
MCP SSE协议发送注意
java
仙俊红1 小时前
SpringBoot启动原理
java·spring boot·后端
星间都市山脉2 小时前
Android STS(Security Test Suite)完整介绍与测试流程
android·java·linux·windows·ubuntu·android studio·androidx
namexingyun2 小时前
拆解Fable 5三重安全护栏:模型路由、蒸馏防护与生物安全分类器的技术原理 - 微元算力(weytoken)
java·人工智能·python·安全·架构·ai编程