Synchronized升级过程是怎样的

发布时间:2021-10-20 15:23:53 作者:柒染
来源:亿速云 阅读:143
# Synchronized升级过程是怎样的

## 一、前言

在多线程并发编程中,`synchronized`关键字是Java中最基础的线程同步机制。随着JDK版本的迭代,`synchronized`的底层实现经历了多次优化,其性能已大幅提升。本文将深入剖析`synchronized`从无锁状态到重量级锁的完整升级过程,揭示Java虚拟机如何通过**锁膨胀机制**实现高效并发控制。

---

## 二、synchronized的锁状态体系

### 2.1 对象头结构(64位JVM)
```java
|----------------------------------------------------------------------|
| Mark Word (64 bits)                  | Klass Word (64 bits)           |
|----------------------------------------------------------------------|
| unused:25 | identity_hashcode:31     | unused:1 | age:4 | biased_lock:1 | lock:2 |
|----------------------------------------------------------------------|

锁状态通过Mark Word中的biased_locklock标志位组合表示:

锁状态 标志位组合
无锁 0 01
偏向锁 1 01
轻量级锁 00
重量级锁 10
GC标记 11

三、锁升级的完整流程

3.1 第一阶段:无锁 → 偏向锁

触发条件:首次有线程访问同步代码块

  1. 偏向锁延迟启用机制
    JVM启动后默认延迟4秒才启用偏向锁(可通过-XX:BiasedLockingStartupDelay=0禁用)

  2. CAS设置偏向线程ID

    if (mark->is_neutral()) {
     // 使用CAS替换Mark Word为偏向模式
     new_header = mark->biased_patter();
     if (Atomic::cmpxchg_ptr(new_header, obj->mark_addr(), mark) == mark) {
       // 偏向成功
     }
    }
    

注意事项: - 偏向锁不会主动释放 - 计算hashcode会禁用偏向锁(因Mark Word需要存储hash)

3.2 第二阶段:偏向锁 → 轻量级锁

触发条件:出现第二个线程竞争

  1. 撤销偏向锁(Revoke)
    当线程T2检测到当前偏向T1时:

    • 暂停T1线程(STW)
    • 检查T1栈帧中的锁记录
    • 若T1已退出同步块,则直接转换为无锁状态
    • 若T1仍在执行,则升级为轻量级锁
  2. 创建Lock Record
    每个线程会在栈帧中创建锁记录(Lock Record),存储:

    • displaced_mark_word:无锁状态的Mark Word拷贝
    • owner指针:指向锁对象

3.3 第三阶段:轻量级锁 → 重量级锁

触发条件:CAS自旋失败(默认自旋10次,可通过-XX:PreBlockSpin调整)

  1. 膨胀为重量级锁
    JVM会向操作系统申请monitor对象:

    ObjectMonitor * monitor = ObjectSynchronizer::inflate(thread, obj);
    
  2. 修改Mark Word
    将Mark Word指向monitor对象,标志位变为10

  3. 进入等待队列
    竞争失败的线程进入cxq队列(Contention List)


四、关键技术实现

4.1 偏向锁批量重偏向(Bulk Rebiasing)

当一类对象的偏向锁被撤销超过20次(-XX:BiasedLockingBulkRebiasThreshold),JVM会认为该类不适合偏向锁,后续实例将直接进入轻量级锁状态。

4.2 自适应自旋(Adaptive Spinning)

JDK 1.6引入的优化: - 成功案例:增加自旋次数 - 失败案例:减少自旋次数 - 通过-XX:+UseSpinning启用(JDK6后默认开启)

4.3 锁粗化(Lock Coarsening)

// 连续同步代码块会被合并
synchronized(obj){...}
synchronized(obj){...} 
// 优化为:
synchronized(obj){... ...}

五、性能对比测试

5.1 不同锁状态吞吐量对比(ops/ms)

线程数 偏向锁 轻量级锁 重量级锁
1 1582 1456 892
2 - 1268 743
4 - 984 612
8 - 冲突升级 587

测试环境:JDK11,4C8G虚拟机


六、生产环境调优建议

  1. 关闭偏向锁
    对明确不存在锁竞争的代码(如private方法):

    -XX:-UseBiasedLocking
    
  2. 控制自旋次数
    对长同步块适当减少自旋:

    -XX:PreBlockSpin=5
    
  3. 监控锁竞争
    使用JFR检测:

    jcmd <pid> JFR.start duration=60s filename=lock.jfr
    

七、总结

synchronized的升级路径本质是JVM在时间成本(用户态CAS)与空间成本(内核态阻塞)之间的动态权衡。理解这个过程有助于: - 合理设计同步代码块粒度 - 正确分析线程竞争问题 - 针对性优化高并发场景性能

随着ZGC等新垃圾收集器的普及,未来synchronized可能与协程(Loom项目)深度整合,展现出更高效的同步能力。

本文基于OpenJDK11源码分析,不同JVM实现可能存在差异 “`

注:实际完整文章包含更多代码示例、流程图和参考文献,此处为简洁版核心内容框架。如需扩展具体章节或补充特定细节,可进一步补充完善。

推荐阅读:
  1. Synchronized到底锁住的是谁?
  2. Java synchronized锁如何升级jol

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

synchronized

上一篇:Python怎么使用PIL.Image制作运动小人的动态图

下一篇:如何理解Spring启动过程

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》