剖析 Java并发关键字synchronized以及优化

synchronized的作用范围

  1. synchronized修饰成员变量和非静态方法,锁的是对象的实例。

    synchronized void method() {
    //业务代码
    }
    
  2. synchronized修饰静态方法时,锁住的是Class实例,因为static是JVM层面的方法,属于Class而不属于对象。

    synchronized void staic method() {
    //业务代码
    }
    
  3. synchronized修饰一个代码块时,锁住的是所有代码块中的对象。

    synchronized(this) {
    //业务代码
    }
    

synchronized实现原理

  1. synchronized同步代码块:synchronized关键字经过编译之后,会在同步代码块前后分别形成monitorenter和monitorexit字节码指令,在执行monitorenter指令的时候,首先尝试获取对象的锁,如果这个锁没有被锁定或者当前线程已经拥有了那个对象的锁,锁的计数器就加1,在执行monitorexit指令时会将锁的计数器减1,当减为0的时候就释放锁。如果获取对象锁一直失败,那当前线程就要阻塞等待,直到对象锁被另一个线程释放为止。
  2. 同步方法:方法级的同步是隐式的,无须通过字节码指令来控制,JVM可以从方法常量池的方法表结构中ACC_SYNCHRONIZED访问标志得知一个方法是否声明为同步方法。当方法调用的时,调用指令会检查方法的ACC_SYNCHRONIZED访问标志是否被设置,如果设置了,执行线程就要求先持有monitor对象,然后才能执行方法,最后当方法执行完(无论是正常完成还是非正常完成)时释放monitor对象。在方法执行期间,执行线程持有了monitor对象,其他线程都无法再次获取同一个monitor对象。

对象头的存储结构以及Mark Word的状态变化。

锁状态存储内容标志位
无锁对象的hashCode、对象分代年龄、是否是偏向锁(0)01
偏向锁偏向线程ID、偏向时间戳、对象分代年龄、是否是偏向锁(1)01
轻量级锁指向栈中锁记录的指针00
重量级锁指向互斥量的指针11

从对象头的存储内容可以看出锁的状态都保存在对象头中。标志位为01的时候锁为无状态,当其从轻量级锁膨胀为重量级锁时,标志位从01变为10。其中指针指向的是monitor对象(也称为管程或监视器锁)的起始地址

总的来说:关于Synchronized的实现在java对象头里较为简单,只是改变一下标识位,并将指针指向monitor对象的起始地址。

Monitor对象

在Java虚拟机(HotSpot)中,monitor是由ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的)。ObjectMonitor中有几个关键属性:

  1. _count用来记录该线程获取锁的次数
  2. WaitSet存放处于wait状态的线程队列
  3. _EntryList存放处于等待获取锁block状态的线程队列,即被阻塞的线程
  4. _owner指向持有ObjectMonitor对象的线程

img

图引用自:深入理解Java并发之synchronized实现原理_zejian的博客-CSDN博客_synchronized原理

当多个线程同时访问一段同步代码时,首先会进入_EntryList队列中,当某个线程获取到对象的monitor后进入_Owner区域并把monitor中的_owner变量设置为当前线程,同时monitor中的计数器 _count加1,若线程调用wait()方法,将释放当前持有的monitor,_owner变量恢复为null,_count自减1,同时该线程进入_WaitSet集合中等待被唤醒。若当前线程执行完毕也将释放monitor(锁)并复位变量的值,以便其他线程进入获取monitor(锁)。

synchronized优化(锁升级过程)

jdk6之前synchronized依赖于底层的操作系统的Mutex Lock来实现的,操作系统实现线程之间切换比较耗时间。JDK6之后为了减少获得锁和释放锁所带来的性能消耗,引入了偏向锁、轻量级锁、自旋锁

偏向锁
轻量级锁
重量级锁
优点缺点适用场景
偏向锁加锁和解锁不需要额外的消耗,和执行非同步方法相比仅存在纳秒级的差距如果线程间存在锁竞争,会带来额外的锁撤销的消耗适用于只有一个线程访问同步块场景
轻量级锁竞争的线程不会阻塞,提高了程序的响应速度如果始终得不到锁竞争的线程,使用自旋会消耗CPU追求响应速度,同步块执行速度非常快
重量级锁线程竞争不使用自旋,不会消耗CPU线程阻塞,响应时间缓慢追求吞吐量,同步块执行速度较慢

锁升级流程:

在这里插入图片描述

参考文章:

1.小白也能看懂的锁升级过程和锁状态_牧小农-CSDN博客

2.深入理解Java并发之synchronized实现原理_zejian的博客-CSDN博客_synchronized原理

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值