您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

不吃饭也要掌握的Synchronized锁升级过程

时间:2023-11-08 14:07:51  来源:微信公众号  作者:小王博客基地

一、前言

在面试题中经常会有这么一道面试题,谈一下synchronized锁升级过程?

之前背了一些,很多文章也说了,到底怎么什么条件才会触发升级,一直不太明白。

实践是检验真理的唯一标准,今天就和大家一起实践一下,什么条件才会升级!

二、为什么会有锁升级过程?

在实践之前,我们先一步步的来了解!为什么要升级呢?

在JDK1.6之前,synchronized的性能一直没有ReentrantLock性能高,主要是因为synchronized涉及到用户态和内核态的切换,这个是在操作系统和硬件是非常消耗资源的。

经过不断的统计分析,发现大部分时间一个锁都是一个线程去获取,如果只有一个线程来尝试加锁,就是重量级锁,显而浪费资源。

「总之,锁的升级过程是为了提高多线程环境下的性能和吞吐量,减少同步操作的开销,并尽量避免线程切换的开销。JAVA虚拟机根据线程竞争的情况和锁的使用情况自动进行锁的升级和降级,以优化多线程程序的性能。」

此时,就引入了很多锁类型,下面我们来具体看看!

三、锁分类

偏向锁:偏向锁是为了解决单线程访问的场景,偏向锁允许第一个访问共享资源的线程获得锁,把线程id存到对象头中,后续的访问可以直接获得锁,而不需要竞争。

轻量级锁:当一个或多个线程尝试获取同一个锁时,偏向锁会升级为轻量级锁。轻量级锁采用CAS(Compare and Swap)操作来减小锁的竞争。采用自适应自旋!

重量级锁:操作系统的调度器会介入,将竞争锁的线程挂起,直到锁被释放为止,重量级锁的开销相对较高。

「补充:」

「自适应自旋的基本思想是根据锁的争用情况,决定线程是否应该自旋等待,以及自旋等待的时间,一般情况为自旋10次。」

四、对象内存结构

我们在说锁的升级过程之前,需要了解一下对象的内存结构,因为在锁升级过程中会往对象头上进行填充信息!一个对象分为:对象头、实例数据、对其填充位三部分组成。

不吃饭也要掌握的Synchronized锁升级过程

我们本次主要用到对象头,我们再看一下详细的对象头信息里有什么:

不吃饭也要掌握的Synchronized锁升级过程

五、图解锁升级过程

先来一个简图:

不吃饭也要掌握的Synchronized锁升级过程

下面引用百度上的一张详细一点的图:

不吃饭也要掌握的Synchronized锁升级过程

我们来详细的说一下锁的升级过程,在每一个锁切换时的条件是什么?

在JDK8时,偏向锁默认是在程序启动后4s自动开启的,在JKD15之后默认是不开启的!

可以设置无延迟时间启动:-XX:BiasedLockingStartupDelay=0也可以不启动偏向锁:-XX:-UseBiasedLocking = false。

直接说有点不形象,我们下面结合代码来实战,看一下具体情况!

六、实战锁升级过程

为了我们能够查询对象结构,我们需要引入jar帮助我们查看!

1、导入依赖

「注意」:不要使用高版本的,高版本不显示2进制,不好观察!

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.10</version>
</dependency>

2、实战代码和解析

我们来从序号1开始,上面也说了默认4s后开启偏向锁,我们会发现序号1打印的对象头序号为:001我们的对象大小为20,内部帮我们补位来满足是8的倍数。方便操作系统进行寻址,不会有碎片组合!这个大家可以详细搜一下,这里就一带而过了哈!

不吃饭也要掌握的Synchronized锁升级过程

此时我们睡眠6s,包装偏向锁开启成功!

我们来到序号2,开启了偏向锁,我们发现对象头序号为:101。

「节点:从无锁到偏向锁切换的条件:JDK8中默认4s后开启,JDK15需要手动开启」。

不吃饭也要掌握的Synchronized锁升级过程

来到序号3和4一起说吧,当我们进行synchronized加锁时,对象的头信息中会记录上当前线程的id,下面再有加锁的,直接判断线程id是否一致,一致直接进入代码块。不一致后面再说!我们发现在序号4时,已经出了代码块,在此查询加锁的对象,信息依旧在,不会进行移除,这就是偏向,直到下一个线程把上一个替换掉!

代码里循环了三次,对象都是一样的!

「节点:在只有一个线程访问代码块的时候,对象中会记录当前线程id。」

不吃饭也要掌握的Synchronized锁升级过程

「以上都是在一个线程来访问的情况下」

来到序号5,我们新建了一个线程来进行加锁。此时会判断当前线程id和新线程id是否一致,不一致就会认为有竞争关系,会立刻切换为轻量级锁。对象头序号为:00

「节点:当有两个线程交替获取锁时,不存在同时竞争获取锁时。」

不吃饭也要掌握的Synchronized锁升级过程

序号6和7一起说,我们让上面序号5这个线程获取锁后睡眠3s,持续获得锁。在开启一个新的线程去竞争获取锁,此时先进行自适应CAS自旋,一般10次后一直没办法获取锁,判定为激烈竞争关系。变为重量级锁,序号7线程会进行放到阻塞队列中。对象头序号为:10。

经过睡眠后,序号6在此获取对象的信息时,已经变为重量级锁!

「节点:有两个及其以上线程同时获取锁,且在自适应自旋范围内没有获取到锁」。

不吃饭也要掌握的Synchronized锁升级过程

下面是代码,大家可以在本地试一下!

/**
 * jvm默认延时4s自动开启偏向锁,
 * 可通过 -XX:BiasedLockingStartupDelay=0
 * 取消延时如果不要偏向锁,可通过-XX:-UseBiasedLocking = false
 * @author wangzhenjun
 * @date 2023/10/18 14:42
 */
public class LockUp {

    @SneakyThrows
    public static void mAIn(String[] args) {

        LockInfo lockInfo = new LockInfo();
        System.out.println("1.无状态:" + ClassLayout.parseInstance(lockInfo).toPrintable());

        Thread.sleep(6000);
        LockInfo lock = new LockInfo();
        System.out.println("2.已经开启了偏向锁模式:" + ClassLayout.parseInstance(lock).toPrintable());

        for (int i = 0; i < 3; i++) {
            synchronized (lock) {
                System.out.println("3.偏向锁模式下,加锁状态:" + ClassLayout.parseInstance(lock).toPrintable());
            }
            System.out.println("4.锁释放了,加锁状态:" + ClassLayout.parseInstance(lock).toPrintable());
        }

        new Thread(() -> {
            synchronized (lock) {
                System.out.println("5.轻量级锁,加锁状态:" + ClassLayout.parseInstance(lock).toPrintable());

                System.out.println("睡眠3s");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("6.轻量级锁=>重量级锁,加锁状态:" + ClassLayout.parseInstance(lock).toPrintable());
            }
        }).start();

        Thread.sleep(1000);

        new Thread(() -> {
            synchronized (lock) {
                System.out.println("重量级锁,加锁状态:" + ClassLayout.parseInstance(lock).toPrintable());
            }
        }).start();

    }
}

七、总结与拓展

经过实战,我们知道了每一个的切换条件,可以在面试中好好地回答了。不至于面试官反问一下就不坚定了!

关于切换到重量级锁后,有兴趣的话,可以下载openJDK源码去看一下关于hotspot/src/share/vm/runtime/objectMonitor.cpp和hotspot/src/share/vm/runtime/objectMonitor.hpp。

源码下载地址:https://github.com/openjdk/jdk8

objectMonitor.cpp:是 OpenJDK 中实现 Java 同步机制的核心部分,它负责管理对象监视器,确保多线程程序能够正确协同工作,实现线程同步和等待/通知机制。

不吃饭也要掌握的Synchronized锁升级过程

objectMonitor.hpp:主要用于定义对象监视器的接口和数据结构,为实际的对象监视器的实现提供了基础。

不吃饭也要掌握的Synchronized锁升级过程



Tags:Synchronized   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
不吃饭也要掌握的Synchronized锁升级过程
一、前言在面试题中经常会有这么一道面试题,谈一下synchronized锁升级过程?之前背了一些,很多文章也说了,到底怎么什么条件才会触发升级,一直不太明白。实践是检验真理的唯一标准...【详细内容】
2023-11-08  Search: Synchronized  点击:(226)  评论:(0)  加入收藏
一起来了解JDK1.8中的CAS与synchronized机制
在JDK1.8 中,ConcurrentHashMap是一个非常重要的线程安全的Map 类型。它采用了CAS 和synchronized 两种机制来实现线程安全,以保证在多线程环境下数据的一致性和正确性。首先,...【详细内容】
2023-11-01  Search: Synchronized  点击:(304)  评论:(0)  加入收藏
你真的了解Java监视器锁和Synchronized关键字吗?
在Java中,多线程并发访问共享资源是一个经常遇到的问题。为了保证数据的正确性和一致性,在多线程编程中需要使用同步机制来实现对临界资源的互斥访问。Java中的synchronized关...【详细内容】
2023-05-05  Search: Synchronized  点击:(442)  评论:(0)  加入收藏
30分钟彻底弄懂 synchronized 锁升级过程
在Java的并发编程领域中,我们进行会使用到锁这个东西,例如在多线程环境下为了预防某些线程安全问题,这里面可能会产生一些预想不到的问题,所以下边我整理了一系列关于JDK中锁的...【详细内容】
2022-07-25  Search: Synchronized  点击:(381)  评论:(0)  加入收藏
你想10分钟吃透synchronized锁的各种用法和注意事项?今天它来了
前言在Java中,synchronized锁可能是我们最早接触的锁了,在 JDK1.5之前synchronized是一个重量级锁,相对于juc包中的Lock,synchronized显得比较笨重。庆幸的是在 Java 6 之后 Jav...【详细内容】
2021-05-14  Search: Synchronized  点击:(425)  评论:(0)  加入收藏
▌简易百科推荐
即将过时的 5 种软件开发技能!
作者 | Eran Yahav编译 | 言征出品 | 51CTO技术栈(微信号:blog51cto) 时至今日,AI编码工具已经进化到足够强大了吗?这未必好回答,但从2023 年 Stack Overflow 上的调查数据来看,44%...【详细内容】
2024-04-03    51CTO  Tags:软件开发   点击:(5)  评论:(0)  加入收藏
跳转链接代码怎么写?
在网页开发中,跳转链接是一项常见的功能。然而,对于非技术人员来说,编写跳转链接代码可能会显得有些困难。不用担心!我们可以借助外链平台来简化操作,即使没有编程经验,也能轻松实...【详细内容】
2024-03-27  蓝色天纪    Tags:跳转链接   点击:(12)  评论:(0)  加入收藏
中台亡了,问题到底出在哪里?
曾几何时,中台一度被当做“变革灵药”,嫁接在“前台作战单元”和“后台资源部门”之间,实现企业各业务线的“打通”和全域业务能力集成,提高开发和服务效率。但在中台如火如荼之...【详细内容】
2024-03-27  dbaplus社群    Tags:中台   点击:(8)  评论:(0)  加入收藏
员工写了个比删库更可怕的Bug!
想必大家都听说过删库跑路吧,我之前一直把它当一个段子来看。可万万没想到,就在昨天,我们公司的某位员工,竟然写了一个比删库更可怕的 Bug!给大家分享一下(不是公开处刑),希望朋友们...【详细内容】
2024-03-26  dbaplus社群    Tags:Bug   点击:(5)  评论:(0)  加入收藏
我们一起聊聊什么是正向代理和反向代理
从字面意思上看,代理就是代替处理的意思,一个对象有能力代替另一个对象处理某一件事。代理,这个词在我们的日常生活中也不陌生,比如在购物、旅游等场景中,我们经常会委托别人代替...【详细内容】
2024-03-26  萤火架构  微信公众号  Tags:正向代理   点击:(10)  评论:(0)  加入收藏
看一遍就理解:IO模型详解
前言大家好,我是程序员田螺。今天我们一起来学习IO模型。在本文开始前呢,先问问大家几个问题哈~什么是IO呢?什么是阻塞非阻塞IO?什么是同步异步IO?什么是IO多路复用?select/epoll...【详细内容】
2024-03-26  捡田螺的小男孩  微信公众号  Tags:IO模型   点击:(8)  评论:(0)  加入收藏
为什么都说 HashMap 是线程不安全的?
做Java开发的人,应该都用过 HashMap 这种集合。今天就和大家来聊聊,为什么 HashMap 是线程不安全的。1.HashMap 数据结构简单来说,HashMap 基于哈希表实现。它使用键的哈希码来...【详细内容】
2024-03-22  Java技术指北  微信公众号  Tags:HashMap   点击:(11)  评论:(0)  加入收藏
如何从头开始编写LoRA代码,这有一份教程
选自 lightning.ai作者:Sebastian Raschka机器之心编译编辑:陈萍作者表示:在各种有效的 LLM 微调方法中,LoRA 仍然是他的首选。LoRA(Low-Rank Adaptation)作为一种用于微调 LLM(大...【详细内容】
2024-03-21  机器之心Pro    Tags:LoRA   点击:(12)  评论:(0)  加入收藏
这样搭建日志中心,传统的ELK就扔了吧!
最近客户有个新需求,就是想查看网站的访问情况。由于网站没有做google的统计和百度的统计,所以访问情况,只能通过日志查看,通过脚本的形式给客户导出也不太实际,给客户写个简单的...【详细内容】
2024-03-20  dbaplus社群    Tags:日志   点击:(4)  评论:(0)  加入收藏
Kubernetes 究竟有没有 LTS?
从一个有趣的问题引出很多人都在关注的 Kubernetes LTS 的问题。有趣的问题2019 年,一个名为 apiserver LoopbackClient Server cert expired after 1 year[1] 的 issue 中提...【详细内容】
2024-03-15  云原生散修  微信公众号  Tags:Kubernetes   点击:(6)  评论:(0)  加入收藏
站内最新
站内热门
站内头条