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.轻量级锁
    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。以后只要不发生竞争, 这个对象就归该线程所有。

    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.小结

相关推荐

  1. Synchronized关键字的底层原理

    2024-04-15 06:48:02       17 阅读
  2. synchronized底层加锁和释放锁的原理

    2024-04-15 06:48:02       12 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-15 06:48:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-15 06:48:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-15 06:48:02       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-15 06:48:02       20 阅读

热门阅读

  1. Day8-Python基础学习之地图和柱状图构建

    2024-04-15 06:48:02       14 阅读
  2. 【入门】图的dfs遍历

    2024-04-15 06:48:02       15 阅读
  3. List和Map的几种初始化方法

    2024-04-15 06:48:02       16 阅读
  4. Qt 窗⼝

    Qt 窗⼝

    2024-04-15 06:48:02      16 阅读
  5. Kali安全

    2024-04-15 06:48:02       21 阅读
  6. 数据库常用语句复建链接记录 枚举类型转换语义

    2024-04-15 06:48:02       18 阅读
  7. 2024.4.17 Python爬虫复习day05 可视化

    2024-04-15 06:48:02       16 阅读
  8. ARM的TrustZone技术

    2024-04-15 06:48:02       42 阅读
  9. 人工智能常见的分类算法

    2024-04-15 06:48:02       15 阅读
  10. Go语言异常处理方式

    2024-04-15 06:48:02       12 阅读
  11. ASP.NET基于BS方式的即时通讯软件的设计与实现

    2024-04-15 06:48:02       17 阅读
  12. [leetcode 链表] 反转链表 vs 链表相交

    2024-04-15 06:48:02       15 阅读
  13. Docker搭建Emby

    2024-04-15 06:48:02       46 阅读