视频课程
小黑屋思过中,禁止观看!
评论并刷新后可见

您需要在视频最下面评论并刷新后,方可查看完整视频

视频课程
立即观看
付费视频

您支付费用,方可查看完整视频

¥{{user.role.value}}
课程视频
开始学习
会员专享

视频合集

史上最强Synchronized锁升级详解

  • 课程笔记
  • 问答交流

上一节课我讲了Synchronized的底层实现原理,这节课我将重点讲解Synchronized的锁升级,这也是需要重点掌握的技术点。

为了助大家掌握好锁升级,本节课我会重点讲解以下6点:

1.Synchronized锁存储

2.Synchronized的锁状态

3.Synchronized的锁升级流程

4.偏向锁的源码

5.偏向锁与轻量级锁获取流程详解

6.Synchronized锁的总结

Synchronized锁存储

史上最强Synchronized锁升级详解-mikechen

每个对象分为三块区域:对象头、实例数据和对齐填充

标记字段:用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等等,它是实现轻量级锁和偏向锁的关键

类型指针:是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例

早期Synchronized的锁机制

早期,Synchronized属于重量级锁效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的Mutex Lock来实现的。

而操作系统实现线程之间的切换时需要从用户态转换到内核态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高。
史上最强Synchronized锁升级详解-mikechen

用户空间:指的就是用户可以操作和访问的空间,这个空间通常存放我们用户自己写的数据等等

而内核空间则是系统内核来操作的一块空间,这块空间里面存放系统内核的函数、接口等

Synchronized的锁升级流程

评论交流
  1. JansenZhang

    synchronized锁升级流程图

  2. 李鸿翼

    奇怪,我做的作业怎么没有显示?提交试试

    • mikechen

      可以的,你只要提交了,我这边就可以看见

  3. 李鸿翼

    synchronized 在jdk1.6开始做了大量优化,其中一项就是锁升级,目的是为了减少所操作的开销,首先是从偏向锁开始的,随着竞争越来越激励,偏向锁会升级为轻量级锁,最终升级到重量级锁。

    偏向锁:目的是优化同一个线程多次申请一个锁的竞争,线程只要从对象头的标志字段判断是否有偏向锁指向它的ID

    轻量级锁: 目的是优化线程交替执行同步代码块,并且同步代码块执行时间比较短的情况。当一个线程持有偏向锁时,另外一个线程通过CAS操作竞争失败时,会升级会轻量级锁。

    重量级锁:当轻量级锁cas如果失败,首先会通过自旋方式来获取,如果自旋重试几次依然失败,会升级为重量级锁。

    其中自旋锁,会一直进行死循环或者固定循环次数,非常占用CPU操作,而自适应自旋锁,会根据上次自旋的次数,来决定本次自旋的次数,自旋的次数不再固定。

  4. JansenZhang

    synchronized锁升级是从jdk1.6版本开始的机制。锁升级的流程就是偏向锁->轻量级锁->重量级锁的机制。
    偏向锁就是进入同步代码块中的线程总是同一个线程,这个时候直接允许当前线程执行同步代码块,不进行线程的切换等操作;如果出现了别的线程来获取锁,则首先采用cas的方式去替换对象头中的ThreadId(这里有个问题:为什么A线程还持有锁的时候就允许B线程去替换threadId,不用考虑当前锁的状态吗?),如果替换成功,则将锁交给新线程,并执行同步代码块。
    基于以上步骤,如果在B线程使用CAS替换ThreadId的时候失败,则会准备撤销偏向锁,撤销偏向锁的时候,会先暂停并检查A线程的状态,判定是否已经退出了同步代码块,如果已经退出了同步代码块,则将偏向锁撤销,重新进入获取锁的流程,即判定对象头中的锁的threadId是否是B线程Id,这里肯定不是,则B线程会再次使用CAS替换对象头中的ThreadId,继续流程。
    如果在暂停检查A线程状态的时候,发现A线程还没有退出同步代码块,则会将当前的偏向锁升级为轻量级锁。
    轻量级锁会让B线程采用CAS自旋的方式获取锁,但在一定的次数(jkd1.5以前版本是10次)以后,如果B线程还是获取不到锁,则将锁升级为重量级锁。如果在一定的次数以内B线程获取到了锁,则让B线程执行同步代码块,结束返回。
    重量级锁就是jvm利用操作系统的互斥锁Mutex Lock来实现的锁,会使用系统内核对线程进行切换操作,因为内核对线程的操作会涉及到用户态->内核态->用户态的转变,在转变的过程中会涉及到线程信息的存储等操作,所以消耗的资源比较多,效率不如偏向锁和轻量级锁高。
    轻量级锁的CAS自旋操作在jdk1.5以后进行了自适应优化,能更快速的判断出是否要将轻量级锁升级到重量级锁。

    • mikechen

      从面试的角度核心都谈到了,最下面你谈到了互斥锁与切换,建议还可以了解我之前谈到的80软中断的细节,这也是经常被大家忽视的一点。

      前面三段话没看明白,建议可以回车换行,或者1、2、3类似这样的数字加回车,或者画一个流程图来表述也许会更好。

  5. 路正银

    JDK1.6之前synchronized使用的是重量级锁,JDK1.6之后进行了优化,拥有了无锁->偏向锁->轻量级锁->重量级锁的升级过程,而不是无论什么情况都使用重量级锁。

    锁升级的优化是针对于不同的同步场景进行的优化,在不存在锁竞争的时候进入同步方法则使用偏向锁,存在竞争时升级为轻量级锁,轻量级锁采用的是自旋锁,如果同步方法执行时间很短的话,采用轻量级锁虽然会占用cpu资源但是相比于使用重量级锁还是更高效的,但是如果同步方法执行时间很长,那么使用轻量级锁自旋带来的性能消耗就比使用重量级锁更严重,这时候就需要升级为重量级锁。

    偏向锁适用于只有一个线程访问同步块的场景,它的速度是最快的,枷锁和解锁不需要额外的消耗
    轻量级锁适用于同步块执行速度非常快的场景,优点是竞争的线程不会阻塞,提高了程序的响应速度(没有线程从用户态到内核态的切换过程),缺点是有可能自旋次数很多 消耗cpu
    重量级锁适用于同步块执行速度较慢的场景,优缺点恰好和轻量级锁相对,优点是不使用自旋,不会消耗cpu,缺点是会发生线程阻塞
    自旋锁和自适应自旋锁,自旋锁是固定自旋次数的,自适应自旋锁的自旋次数是可变的

    • mikechen

      synchronized锁的优化升级是面试的一大考点,最核心的:偏向锁、轻量级锁、重量级锁的升级过程都谈到了,这一点基本没有什么问题 ✗咧嘴笑✗ 。

      建议还可以对你最后谈到的自旋锁与自适应自旋锁的细节还可以补充,比如1.5之前和之后自旋锁做了哪些改进点。