Synchronized底层如何实现?锁升级过程

6 篇文章 0 订阅

1、Synchronized底层是通过monitor对象来实现的。通过motitorenter和monitorexit方法来实现锁的开启和释放
2、锁升级过程
Java早期,synchronized属于重量级锁,因为在切换线程时实现用户态到核心态的转换,需要较长时间,时间成本较高。在jdk1.6之后,从JVM层面对synchronized进行了较大优化,引入了偏向锁、轻量级锁。(锁可以升级但不能降级,但偏向锁可以可以被重置为无所状态)

偏向锁升级过程:

1、Load-and-test,也就是简单判断一下当前线程id是否与java对象头中Markword当中的线程id是否一致.
2、如果一致,则说明此线程已经成功获得了锁,继续执行下面的代码.
3、如果不一致(其他线程,如线程2要竞争锁对象,而偏向锁不会主动释放因此还是存储的线程1的threadID),那么需要查看Java对象头中记录的线程1是否存活;
4、如果没有存活,那么锁对象被重置为无锁状态,其它线程(线程2)可以竞争将其设置为偏向锁;如果存活,那么立刻查找该线程(线程1)的栈帧信息,如果还是需要继续持有这个锁对象,那么暂停当前线程1,撤销偏向锁,升级为轻量级锁,如果线程1 不再使用该锁对象,那么将锁对象状态设为无锁状态,重新偏向新的线程。

偏向锁是针对于一个线程而言的,线程获得锁之后就不会再有解锁等操作了,这样可以省略很多开销。假如有两个线程来竞争该锁话,那么偏向锁就失效了,进而升级成轻量级锁了。这也就是经常所说的锁膨胀。

在Jdk1.6中,偏向锁的开关是默认开启的,适用于只有一个线程访问同步块的场景。

轻量级锁原理和升级过程

线程1获取轻量级锁时会先把锁对象的对象头MarkWord复制一份到线程1的栈帧中创建的用于存储锁记录的空间(称为DisplacedMarkWord),然后使用CAS把对象头中的内容替换为线程1存储的锁记录(DisplacedMarkWord)的地址;

如果在线程1复制对象头的同时(在线程1CAS之前),线程2也准备获取锁,复制了对象头到线程2的锁记录空间中,但是在线程2CAS的时候,发现线程1已经把对象头换了,线程2的CAS失败,那么线程2就尝试使用自旋锁来等待线程1释放锁。 自旋锁简单来说就是让线程2在循环中不断CAS

但是如果自旋的时间太长也不行,因为自旋是要消耗CPU的,因此自旋的次数是有限制的,比如10次或者100次,如果自旋次数到了线程1还没有释放锁,或者线程1还在执行,线程2还在自旋等待,这时又有一个线程3过来竞争这个锁对象,那么这个时候轻量级锁就会膨胀为重量级锁。重量级锁把除了拥有锁的线程都阻塞,防止CPU空转。

锁标志位”00”表示轻量级锁
轻量级锁主要有两种

自旋锁
自适应自旋锁

当有多个线程同时竞争锁时,会有一个自旋的过程,即让线程循环一直尝试去获取锁,而自适应自旋锁则是自动根据上一次获取到锁的时间调整自旋的时间。
synchronized几种锁的优缺点

总结:Java中的synchronized有偏向锁、轻量级锁、重量级锁三种形式,分别对应了锁只被一个线程持有、不同线程交替持有锁、多线程竞争锁三种情况。

参考资料
参考资料2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值