Java并发编程基础-锁-synchronized状态变换

本文详细解析了JDK1.6中synchronized锁的优化机制,包括偏向锁、轻量锁和重量锁的状态转换过程,以及这些锁如何提升多线程环境下获取和释放锁的效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      synchoronized在Jdk1.6之前一直被称为重量锁,性能较差,很少被推荐使用。synchoronized在Jdk1.6中进行了大量优化,性能与JUC中的各种锁无差,也被逐渐的被大众所接受。

      synchoronized将锁状态记录在了对象头的MarkWord中,包含4种锁状态:无锁、偏向锁、轻量锁和重量锁,锁状态只能升级不能降级,即 无锁-->偏向锁-->轻量锁-->重量锁,目的是提高获取锁和释放锁的效率,对象几种状态的获取和释放如下:

  1. 偏向锁
          大多数情况下,锁不存在多线程竞争,而总是由同一线程获得,为了让线程获得锁的代价更低(消除了同步)而引入了偏向锁。当对象第一次被线程获取的时候,会把MarkWord中的偏向标识位设为01(偏向锁状态),同时使用CAS操作将该线程的ID记录在MarkWord中,如果CAS操作成功,则该线程每次进入到这个锁相关的同步块时,虚拟机都可以不进行任何同步操作。
          当有另外的线程获取这个锁时,偏向模式就结束了,根据锁对象目前是否处于锁定的状态,会恢复到无锁状态(01)或者变为轻量锁状态(00),借用《Java并发编程艺术》的一张图,如下所示:

    cd81a1fc5268eba1508d8ad07cb0f2abc75.jpg
     
  2. 轻量锁
          线程在执行同步块之前,JVM会先在当前线程栈帧中创建用于存储锁记录的空间,并将对象头中的MarkWord复制到锁记录中,官方称为Displaced Mark Word。然后线程尝试使用CAS操作将对象头中的MarkWord替换为指向锁记录的指针,如果成功,当前线程获得锁,如果失败,表示有其他线程竞争锁,当前线程开始自旋,如果自旋完成一定次数(可能是50次、100次,可以通过启动参数配置自旋次数)当前线程还没有获取到锁,那么锁会开始膨胀,进化为重量锁。
          轻量锁解决时,使用CAS操作将Displace Mark Word 替换会对象头,如果成功,则表示没有竞争,如果失败(已经有线程将锁的状态修改为重量锁),则释放锁并唤醒等待线程,借用《Java并发编程艺术》的一张图,如下所示:

    e338f01f6fb4ec9d93b6812e5d24a5532e0.jpg
     
  3. 重量锁
          由上面的轻量锁升级而来,一旦升级到重量锁,那么其他线程获取锁,都会被阻塞,等待释放锁。当持有锁的线程释放锁并唤醒等待线程时,等待的线程就会开始新一轮的竞争。

理解:

  • 当单个线程进入同步块,使用偏向锁,成本低,不需要使用CAS操作
  • 当两个线程依次进入同步块,使用轻量锁,单线程自旋也不会造成太多的CPU损耗
  • 当两个或多个线程进入同步快,使用重量锁,让获取锁的线程之外的线程进行阻塞等待唤醒,效率更高。

PS:使用openjdk的JOL(Java Object Layout)工具可以观察各种线程竞争锁时,Java对象头的状态。

参考书籍及网址:

  • 《Java并发编程艺术》

PS:研究基于MAC+Idea+JDK1.8 64位

Keep Calm and Carry on!

转载于:https://my.oschina.net/gordonfor/blog/2051786

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值