主线程进入,锁升级为 00(轻量级锁) OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 线程 1 和线程 2 同时进入 ,存在竞争 ,升级位重量级锁 10 OFFSET SIZE TYPE DESCRIPTION VALUE 运行 hashcode 后,锁状态升级位无锁不可偏向 ,原本存放线程 ID 的位置被 hashcode 覆盖 OFFSET SIZE TYPE DESCRIPTION 线程 1 进入,无锁竞争,锁升级为轻量锁 000 OFFSET SIZE TYPE DESCRIPTION VALUE 0 线程 2 进入,存在锁竞争,锁升级为轻量锁 010 OFFSET SIZE TYPE DESCRIPTION VALUE 0
知道了 synchronized 的概念,回头来看标题,它说的锁升级到底是个啥?对于不太熟悉锁升级的人来说,可能会想: 所谓锁,不就是啪一下锁上就完事了吗?升级是个什么玩意?这跟打扑克牌也没关系啊。 对于熟悉的人来说,可能会想: 不就是「无锁 ==> 偏向锁 ==> 轻量级锁 ==> 重量级锁 」吗? 你可能在很多地方看到过上面描述的锁升级过程,也能直接背下来。 随着锁竞争逐渐激烈,其状态会按照「无锁 ==> 偏向锁 ==> 轻量级锁 ==> 重量级锁 」这个方向逐渐升级,并且不可逆,只能进行锁升级,而无法进行锁降级。 锁升级 了解完 4 种锁状态之后,我们就可以整体的来看一下锁升级的过程了。 锁升级过程 EOF 其实偏向锁还有一个撤销的过程,也是有代价的,但相比于偏向锁带好的好处,是能够接受的。但我们这里重点的还是关注锁升级的具体逻辑和细节,关于锁升级的过程就聊到这里。
Synchronized锁原理和锁升级过程 Synchronized原理 1.Synchronized修饰范围 修饰静态方法 此时锁的是位于元空间的Class字节码文件,也叫Class模板 修饰实例方法 Synchronized锁升级过程 无锁状态、偏向锁、轻量级锁、重量级锁 ,这是锁膨胀的过程,不可逆,但只有偏向锁可以变回无锁态。 这个锁会偏向于第一个获得它的线程,在接下来的执行过程中,假定没有其他线程来竞争,所以持有偏向锁的线程将永远不需要进行同步操作。 让有其他线程来竞争时就会要么自己已经执行完毕锁偏向给了其他线程,要么竞争失败,锁会在一个安全点上撤销,并升级委轻量级锁。 锁的详细升级过程 1.一开始对象是无锁状态的 2.一个线程尝试执行Synchronize代码块时,成功获得对象的锁,通过CAS操作往该对象markword中插入当前线程id, 同时修改偏向锁的标志位 。
前言在 Java 并发编程领域,synchronized关键字堪称保障线程安全的中流砥柱。随着 JDK 版本的迭代演进,synchronized锁的性能优化也在持续推进,其中锁升级机制尤为关键。 在撤销过程中,JVM 会遍历持有该对象的线程栈,检查是否存在该对象的锁记录。若存在,则将偏向锁升级为轻量级锁。 五、锁升级过程总结在 Java 中,synchronized锁的升级过程是一个逐步优化的过程。当一个线程首次访问同步块时,JVM 会尝试使用偏向锁提升性能。 在实际开发中,我们应尽量避免不必要的锁竞争,合理运用synchronized关键字,充分发挥锁升级机制的优势。六、JDK 版本与锁升级锁升级机制在不同的 JDK 版本中存在差异。 七、总结本文深度剖析了 Java 中synchronized锁的升级过程,涵盖偏向锁、轻量级锁和重量级锁的含义、业务场景、实现方式、模拟示例以及底层原理。
synchronized锁升级 博主 默语带您 Go to New World. 标志位: 偏向锁标志:0 锁标志:01 偏向锁 特点:偏向锁旨在优化单线程情况下的锁获取开销,避免CAS操作。 获取过程: 检查锁标志为 01 且偏向锁标志为 0。 偏向锁的升级 如果锁竞争发生,偏向锁可能升级为轻量级锁。 轻量级锁 特点:通过自旋等待来实现,适合短时间内无大量竞争的场景。 获取过程: 持有偏向锁的线程保存 MarkWord 到线程的锁记录(Lock Record)。 设置 MarkWord 指向线程的锁记录。 标志位为 00。 撤销:轻量级锁会在使用后主动撤销,将 MarkWord 恢复原值。 升级:若自旋超出限定次数或有大量线程竞争,升级为重量级锁。
锁主要存在四中状态,依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着竞争的激烈而逐渐升级。 反之,如果某个锁很少有自旋成功,那么以后的自旋的次数会减少甚至省略掉自旋过程,以免浪费处理器资源。 它的意思就是说,这个锁会偏向于第一个获得它的线程,在接下来的执行过程中,假如该锁没有被其他线程所获取,没有其他线程来竞争该锁,那么持有偏向锁的线程将永远不需要进行同步操作。 缺点:同自旋锁相似:如果锁竞争激烈,那么轻量级将很快膨胀为重量级锁,那么维持轻量级锁的过程就成了浪费。 七、重量级锁 轻量级锁膨胀之后,就升级为重量级锁了。 ,然后重新偏向新的线程,如果原来的线程依然存活,则马上执行那个线程的操作栈,检查该对象的使用情况,如果仍然需要持有偏向锁,则偏向锁升级为轻量级锁,(偏向锁就是这个时候升级为轻量级锁的)。
synchronized锁:由对象头中的Mark Word根据锁标志位的不同而被复用及锁升级策略 2、Synchronized的性能变化 java5以前,只有Synchronized,这个是操作系统级别的重量级操作 3、java6开始,优化Synchronized Java 6之后,为了减少获得锁和释放锁所带来的性能消耗,引入了轻量级锁和偏向锁 需要有个逐步升级的过程,别一开始就捅到重量级锁 3、Synchronized 锁种类及升级步骤 1、多线程访问情况,3种 1)只有一个线程来访问,有且唯一Only One 2)有2个线程A、B来交替访问 3)竞争激烈,多个线程来访问 2、升级流程 synchronized用的锁是存在 锁升级过程总结:一句话,就是先自旋,不行再阻塞。 JDK1.6之前synchronized使用的是重量级锁,JDK1.6之后进行了优化,拥有了无锁->偏向锁->轻量级锁->重量级锁的升级过程,而不是无论什么情况都使用重量级锁。
64操作系统下,Mark Word的长度是64,在加Klass Word(32位),一共是96位,其实对象头长什么样其实不是本文的重点,本文的重点是验证锁升级的过程,所以我们只需要关注对象头中Mark 锁升级的过程 锁状态 25bit 4bit 1bit 2bit 23bit 2bit 是否偏向锁 锁标志位 1 无锁 对象的HashCode 分代年龄 0 01 2 无锁 对象的HashCode 上面表格中2->3的过程。 参考 这可能是B站上最深入解析的synchronized底层原理解析_哔哩哔哩_bilibili 视频去哪了呢? _哔哩哔哩_bilibili synchronized锁原理分析(一、从Java对象头看synchronized锁的状态)_liujianyangbj的博客-CSDN博客 Java的对象头和对象组成详解
如果该线程正在执行同步代码块时有其他线程在竞争(指其他线程尝试 CAS 让 Mark Word 设置自己的线程 ID),会被升级为轻量级锁。 如果成功,表示线程拿到了锁。如果失败,则进行自旋(自旋锁),自旋超过一定次数时升级为重量级锁,这时该线程会被内核挂起。 升级为重量级锁时会在堆中创建 monitor 对象,并将 Mark Word 指向该 monitor 对象。 图片来自:死磕Synchronized底层实现--重量级锁 锁升级的流程图 ? 图片来自:Java Synchronised机制 0x05:锁降级 Hotspot 在 1.8 开始有了锁降级。 死磕Synchronized底层实现--概论 中: 因为在Java中任意对象都可以用作锁,因此必定要有一个映射关系,存储该对象以及其对应的锁信息(比如当前哪个线程持有锁,哪些线程在等待)。
意味着偏向锁升级成轻量级锁后不能降级成偏向锁。 synchronized锁的升级过程(锁/对象状态)通过上述对象头介绍,应该清楚了,synchronized加锁主要改变的是对象头的信息,改变的是64位对象头,最后的三位。 代码演示synchronized锁的升级过程synchronized加锁,一把锁,在没有竞争的情况下,被同一个对象多次获取,所以没必要一直加锁操作,以此来减少CPU资源,所以就会导致加了锁,最后三位数还是 jdk优化之后synchronized加锁,不会立即升级成重量级锁,只有在多个线程并发,加锁,对象状态才会升级重量级锁。 // 线程1,先占有锁,如果有其他线程过来抢占,就会升级重量级锁,没有其他线程抢占,就是轻量级锁 Thread t1 = new Thread(() -> { synchronized
JDK1.6 之后 默认开启如下锁无锁状态偏向锁轻量级锁重量级锁图片随着锁的竞争,锁可以从 偏向锁 升级到 轻量级锁,再升级到 重量级锁但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级Java CAS 原子指令,而偏向锁只需要在置换 ThreadID 的时候依赖一次 CAS 原子指令轻量级锁是为了在线程交替执行同步块时提高性能,而偏向锁则是在只有一个线程执行同步块时进一步提高性能偏向锁获取过程 ,如果存在同一时间访问同一锁的情况,就会导致轻量级锁膨胀为重量级锁轻量级锁的加锁过程(1)在代码进入同步块的时候,如果同步对象锁状态为无锁状态(锁标志位为 “01” 状态,是否为偏向锁为 “0”)虚拟机首先将在当前线程的栈帧中建立一个名为锁记录 (此时锁已膨胀)那就要在释放锁的同时,唤醒被挂起的线程适应性自旋自旋锁从轻量级锁获取的流程中我们知道,当线程在获取轻量级锁的过程中执行 CAS 操作失败时,是要通过自旋来获取重量级锁的问题在于,自旋是需要消耗 的优化但是这两种锁也不是完全没缺点的,比如竞争比较激烈的时候,不但无法提升效率,反而会降低效率,因为多了一个锁升级的过程这个时候就需要通过 -XX:-UseBiasedLocking 来禁用偏向锁几种锁的对比图片锁升级代码演示创建一个
然而,尽管synchronized在日常使用中简单方便,但它的内部实现及其升级过程却蕴藏着极大的复杂性和精妙之处。 在这本期文章中,以JDK17u为例,我将详细记录对synchronized底层机制及其锁升级过程的探究和学习过程。 重量级锁会导致竞争锁的线程进入BLOCKED状态,只有等待获取锁的线程释放锁后,其他线程才能获取到锁 synchronized的这四种锁状态是在JVM层面自动进行的,深入理解synchronized锁升级过程可以帮助开发者更好地对程序进行性能调优或设计更好的并发控制策略 } } 这一块时序图所基于的理论是“当尝试获取锁失败时,synchronized才会发生升级”,synchronized的锁升级过程是一种优化策略,其目的是在不同的竞争条件下尽可能减少系统的开销 这个过程是尝试性的,如果CAS操作失败,表示有其他线程同时尝试获取锁,那么这个synchronized就可能会选择性地升级到重量级锁(在HotSpot JVM中,这种选择是由JVM决定的)。
synchronized实现原理及锁升级过程 QQ截图20190730134117.png 前言: synchronized是Java内置的机制,是JVM层面的,而Lock则是接口,是JDK层面的 尽管最初synchronized的性能效率比较差,但是随着版本的升级,synchronized已经变得原来越强大了,本文带大家了解的是synchronized实现原理及锁升级过程,希望可以帮助到大家。 32位JVM的Mark Word可能变化存储为以下5种数据: 锁一共有四种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态随着竞争情况逐渐升级。 为了提高获得锁和释放锁的效率,锁可以升级但不能降级,意味着偏向锁升级为轻量级锁后不能降级为偏向锁。 如果失败,表示当前锁存在竞争,锁已经被升级为重量级锁,则会释放锁并唤醒等待的线程。
} 锁升级 首先过一下synchronized锁升级的过程 1.偏向锁 当只有一个线程获得了锁,锁就进入偏向模式,MarkWord标识偏向状态,当这个线程再次请求锁时,无需再做任何同步操作,即获取锁的过程 原始的synchronized是直接使用重量级锁,才会导致性能很低,加入锁升级才使得synchronized性能获得很大提升。 理解 以上讲解了synchronized锁升级的过程,如果不好理解,还是拿现实生活举个例子: 假设某公司有多个会议室,每个团队需要获取到会议室的锁才能进去开会,会议室门口挂着一个写字板,时刻记录当前会议室使用状态 这就是轻量级锁,偏向锁出现了竞争会升级为轻量级锁,因为大部分线程占用锁的时间不会特别长,所以等待线程刚开始不需要挂起,只需要通过空转自旋等待,一般很快就会获取到锁,比过程一直占用着cpu。 这就是重量级锁,其中会议室管理员相当于操作系统,当某个线程自旋次数过多或者多个线程同时竞争锁,锁竞争变的激烈,轻量级锁升级为重量级锁,此时等待线程都挂起,对象锁释放后再由操作系统唤醒线程,此过程开销很大
synchronized 是 Java 中实现线程同步的核心机制,其锁状态会随着竞争激烈程度动态升级,以提高并发性能。从 无锁 到 重量级锁 的升级过程包括四个阶段,且 不可逆。 后续同一线程访问时,无需 CAS 操作,直接检查线程 ID 即可快速获取锁 升级条件: 当其他线程尝试竞争锁时,偏向锁撤销(需等待全局安全点),并升级为轻量级锁3. 未获取锁的线程进入阻塞队列,由操作系统调度唤醒缺点: 涉及用户态到内核态的切换,性能开销较大锁升级的关键特性不可逆性: 锁只能从低级别(如偏向锁)升级到高级别(如重量级锁),无法降级。 监控锁状态: 使用工具(如 JOL)查看对象头信息,分析锁升级过程替代方案: 在高并发场景下,可结合 ReentrantLock 或 StampedLock 提升灵活性通过理解锁升级机制,开发者可以更好地优化多线程程序的性能 4s),确保偏向锁生效 Thread.sleep(5000); // 首次获取锁,触发偏向锁 synchronized (obj) {
锁升级 由于synchronized性能问题在JDK1.6前饱受诟病,同时和@author Doug Lea大神写的目前在JUC下的AQS实现的锁差距太大,synchronized开发人员感觉脸上挂不住 升级过程 ? 结果可以看出:所有线程结束后已经不存在竞争时并不会变为无锁状态,也就是说锁只能升级,不能降级,竞争比较严重时升级为重量级锁,偏向锁和轻量级锁在用户态维护不需要经过内核态,重量级锁需要切换到内核态(os) 轻量级锁 在锁升级过程中有一个轻量级锁,轻量级锁一般指的就是自旋锁CAS(Compare And Exchange),对java开发者来说这种锁也可以看成无锁,因为在java代码层面没有锁的代码。 最后留下几个问题思考 简述锁升级过程? 自旋锁什么时候升级为重量级锁? 为什么有自旋锁还需要重量级锁? 偏向锁是否一定比自旋锁效率高?
在 32 位的虚拟机中: 在 64 位的虚拟机中: 上图中的偏向锁和轻量级锁都是在 java6以后对锁机制进行优化时引进的,下文的锁升级部分会具体讲解,synchronized 关键字对应的是重量级锁, 锁的优化 锁的 4 中状态:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态(级别从低到高),整个锁的状态从低到高变化的过程被称为所升级。 为什么要引入偏向锁? 轻量级锁什么时候升级为重量级锁? 重量级锁把除了拥有锁的线程都阻塞,防止 CPU 空转。」 ❝ 注意:为了避免无用的自旋,轻量级锁一旦膨胀为重量级锁就不会再降级为轻量级锁了;偏向锁升级为轻量级锁也不能再降级为偏向锁。 一句话就是锁可以升级不可以降级,但是偏向锁状态可以被重置为无锁状态。
synchronized是jvm内部的一把隐式锁,一切的加锁和解锁过程是由jvm虚拟机来控制的,不需要我们认为的干预,我们大致从了解锁,到synchronized的使用,到锁的膨胀升级过程三个角度来说一下 我们还可以将synchronized锁放置在方法上。 说到这我们就可以来看一下我们锁的膨胀升级过程了。 锁的膨胀升级 我们说过了对象头的内容,接下来可以说说我们的锁内部是如何升级上锁的了。 从无锁到重量级锁的一个升级过程,我们来边画图,边详细看一下。 无锁状态: ? 然后线程B也会不甘示弱啊,会尝试修改一次23bit位的对象头存储,如果说这时恰好线程A释放了锁,可以修改成功,然后线程B就可以持有该偏向锁了。如果修改失败,开始升级锁。
在 32 位的虚拟机中: 在 64 位的虚拟机中: 上图中的偏向锁和轻量级锁都是在 java6以后对锁机制进行优化时引进的,下文的锁升级部分会具体讲解,synchronized 关键字对应的是重量级锁 锁的优化 锁的 4 中状态:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态(级别从低到高),整个锁的状态从低到高变化的过程被称为所升级。 为什么要引入偏向锁? 轻量级锁什么时候升级为重量级锁? 重量级锁把除了拥有锁的线程都阻塞,防止 CPU 空转。」 ❝ 注意:为了避免无用的自旋,轻量级锁一旦膨胀为重量级锁就不会再降级为轻量级锁了;偏向锁升级为轻量级锁也不能再降级为偏向锁。 一句话就是锁可以升级不可以降级,但是偏向锁状态可以被重置为无锁状态。
在前面聊过了如何使用synchronized,以及synchronized不同的加锁方式分别锁的是哪些对象。本文对synchronized底层的原理进行深层次的分析。 3.synchronized的锁升级简介 再synchronized的执行过程中,实际上一个对象的状态就如上表所示进行变化: 无锁:所有对象创建的时候都是无锁状态。 轻量级锁:当锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,从而提高性能。 这就是synchronized锁升级的过程: ? 需要注意的是: 偏向锁只会在第一次请求的时候采用cas操作,修改锁的对象和记录线程的地址。 后续将通过源码,对synchronized的加锁和升级过程进行分析。