盒子
盒子
文章目录
  1. 1. synchronized实现同步的基础
  2. 2. synchronized实现原理
  3. 3. synchronized锁存放位置
  4. 4. synchronized锁的升级与对比
    1. 4.1 偏向锁
    2. 4.2 轻量级锁
    3. 4.3 锁优缺点对比

Java并发机制底层实现原理之synchronized

1. synchronized实现同步的基础

java中的每一个对象都可以作为锁。具体有3种表现形式:

  • 普通的同步方法,锁是当前的实例对象
  • 静态的同步方法,锁是当前类class对象
  • 同步方法块,锁是synchronized括号里配置的对象

2. synchronized实现原理

JVM基于进入和退出Monitor对象来实现方法同步与代码块同步,代码块同步是使用monitorenter和monitorexit指令来实现,而方法同步实现方式未在JVM规范中指出,但是也可以使用这两个指令实现。

3. synchronized锁存放位置

synchronized锁存放在Java对象头里。如果对象是数组类型,则虚拟机用3个字宽(Word)存储对象头,如果对象是非数组类型,则用2个字宽存储对象头。32位虚拟机中,1字宽为4字节,即32bit
Java对象头里的Mark Word里默认存放的是HashCode、分代年龄和锁标记位。

4. synchronized锁的升级与对比

在JDK1.6及以上,锁共有4中状态,级别从低到高一次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,这几个状态会根据竞争情况逐步升级,但不能降级(目的是为了提高获取锁和释放锁的效率)。

4.1 偏向锁

  • 加锁: 把对象头与栈帧的锁记录里存储锁偏向的线程ID。以后进入时测试对象头Mark Word中是否存储了指向当前线程的偏向锁,如果成功,可重入,无需CAS操作进行加锁和释放锁;如果失败,则测试Mark Word中偏向锁的标识是否为1(表明当前是偏向锁):如果没有,则使用CAS竞争,否则,使用CAS将对象头的偏向锁指向当前线程

  • 解锁:需要等待全局安全点(在这个时间点上没有执行的字节码)。首先暂停拥有偏向锁的线程,然后检查持有偏向锁的线程是否存活,若否,则将对象头设为无锁状态;否则,拥有偏向锁的栈会被执行,遍历偏向对象的锁记录,栈中的锁记录和Mark Word要么重新偏向其他线程,要么恢复为无锁或者标记为不适合为偏向锁,最后唤醒暂停的线程。

  • 关闭: JVM参数设置: -XX:BiasedLockingStartupDelay=0 关闭延迟激活偏向锁 -XX UserBiasedLocking=false 关闭偏向锁

4.2 轻量级锁

  • 加锁: 线程在执行同步代码块之前,会在当前线程栈帧中创建用于存储锁记录的空间,并将Mark Word复制到所记录中,并使用CAS尝试将Mark Word替换为指向所记录的指针。如果成功,当前线程获得锁,如果失败,则自旋获取锁。
  • 解锁: 使用CAS将Displaced Mark Word替换回对象头,若失败,则会膨胀为重量级锁

4.3 锁优缺点对比

优点 缺点 适用场景
偏向锁 加锁和解锁不需要额外的消耗 如果存在竞争,会带来额外解锁消耗 适用一个线程访问同步块场景
轻量级锁 竞争的线程不会阻塞,提高程序响应速度 始终得不到锁竞争的线程,自旋消耗CPU 追求响应时间,同步块响应非常快
重量级锁 线程竞争不使用自旋,不会消耗CPU 线程阻塞,响应时间慢 追求吞吐量,同步块执行速度较长
支持一下
扫一扫,支持沈健
  • 微信扫一扫
  • 支付宝扫一扫