news 2026/3/16 14:41:23

Synchronized锁升级流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Synchronized锁升级流程

文章目录

    • 引言
    • synchronized的基本使用
      • 同步方法
      • 同步代码块
    • synchronized的底层原理
      • 字节码层面分析
      • 对象头与Mark Word
    • 锁升级优化过程
      • 1. 偏向锁(Biased Locking)
      • 2. 轻量级锁(Lightweight Locking)
      • 3. 重量级锁(Heavyweight Locking)
    • 实战中的最佳实践
      • 1. 锁粒度控制
      • 2. 避免死锁
      • 3. 双检锁单例模式
    • 性能优化建议
      • 1. 减少锁持有时间
      • 2. 使用读写锁替代
    • 常见问题与解决方案
      • 1. synchronized与Lock的区别
      • 2. 如何选择锁策略
    • 总结

引言

线程安全是我们必须面对的核心挑战之一。Java为我们提供了synchronized关键字。

synchronized的基本使用

同步方法

同步方法是最简单的使用方式,直接在方法声明中添加synchronized关键字即可:

publicclassCounter{privateintcount=0;// 同步实例方法publicsynchronizedvoidincrement(){count++;}// 同步静态方法publicstaticsynchronizedvoidstaticIncrement(){// 静态方法的锁是类的Class对象}}

代码说明:

  • 实例方法的锁是当前对象实例(this)
  • 静态方法的锁是当前类的Class对象
  • 同步方法保证了同一时间只有一个线程能执行该方法

同步代码块

同步代码块提供了更细粒度的控制,可以指定锁对象:

publicclassOrderService{privatefinalObjectlock=newObject();privateMap<String,Integer>inventory=newHashMap<>();publicvoidprocessOrder(StringproductId){// 非同步代码,可以并发执行System.out.println("开始处理订单...");synchronized(lock){// 同步代码块,保证库存操作的原子性Integerstock=inventory.get(productId);if(stock!=null&&stock>0){inventory.put(productId,stock-1);System.out.println("扣减库存成功");}}// 后续非同步操作System.out.println("订单处理完成");}}

代码说明:

  • 可以指定任意对象作为锁
  • 锁的范围更小,性能更好
  • 提供了更灵活的同步控制

synchronized的底层原理

字节码层面分析

让我们通过反编译来看看synchronized在字节码层面是如何实现的:

publicclassSynchronizedDemo{privatestaticintcounter=0;privatefinalObjectlock=newObject();publicvoidsyncMethod(){synchronized(this){counter++;}}}

使用javap -c SynchronizedDemo.class反编译后,可以看到关键字节码:

public void syncMethod(); Code: 0: aload_0 1: dup 2: astore_1 3: monitorenter // 进入同步块 4: getstatic #2 // 获取counter 7: iconst_1 8: iadd 9: putstatic #2 // 设置counter 12: aload_1 13: monitorexit // 正常退出同步块 14: goto 22 17: astore_2 18: aload_1 19: monitorexit // 异常退出同步块 20: aload_2 21: athrow 22: return

关键点解析:

  • monitorenter:获取对象的监视器锁
  • monitorexit:释放对象的监视器锁
  • 编译器会自动生成异常处理,确保锁一定会被释放

对象头与Mark Word

在HotSpot虚拟机中,每个对象都有一个对象头,其中包含Mark Word,它记录了对象的锁状态信息:

锁状态存储内容标志位
无锁对象哈希码、分代年龄01
偏向锁线程ID、Epoch、分代年龄01
轻量级锁指向栈中锁记录的指针00
重量级锁指向互斥量(monitor)的指针10
GC标记11

锁升级优化过程

JDK 1.6之后,synchronized引入了锁升级机制来优化性能:

1. 偏向锁(Biased Locking)

publicclassBiasedLockExample{privatestaticfinalObjectlock=newObject();privatestaticintcount=0;publicstaticvoidmain(String[]args)throwsInterruptedException{// 默认情况下,JVM会延迟开启偏向锁Thread.sleep(5000);// 等待偏向锁开启synchronized(lock){count++;System.out.println("第一次获取锁,应该是偏向锁");}}}

偏向锁特点:

  • 适用于只有一个线程访问同步块的场景
  • 在对象头中记录线程ID
  • 同一个线程再次获取锁时不需要CAS操作

2. 轻量级锁(Lightweight Locking)

当有第二个线程尝试获取锁时,偏向锁会升级为轻量级锁:

publicclassLightweightLockExample{privatestaticfinalObjectlock=newObject();publicstaticvoidmain(String[]args){// 线程1newThread(()->{synchronized(lock){try{Thread.sleep(100);// 短暂持有锁}catch(InterruptedExceptione){e.printStackTrace();}}}).start();// 线程2 - 会触发锁升级newThread(()->{try{Thread.sleep(10);// 确保线程1先获取锁}catch(InterruptedExceptione){e.printStackTrace();}synchronized(lock){System.out.println("线程2获取锁,此时应该是轻量级锁");}}).start();}}

轻量级锁特点:

  • 使用CAS操作替代操作系统互斥量
  • 适用于线程交替执行的场景
  • 自旋等待避免线程切换开销

3. 重量级锁(Heavyweight Locking)

当竞争激烈时,轻量级锁会升级为重量级锁:

publicclassHeavyweightLockExample{privatestaticfinalObjectlock=newObject();privatestaticfinalintTHREAD_COUNT=10;publicstaticvoidmain(String[]args){CountDownLatchlatch=newCountDownLatch(THREAD_COUNT);for(inti=0;i<THREAD_COUNT;i++){newThread(()->{synchronized(lock){try{// 模拟业务处理Thread.sleep(50);}catch(InterruptedExceptione){e.printStackTrace();}}latch.countDown();}).start();}try{latch.await();System.out.println("所有线程执行完成,经历了锁升级过程");}catch(InterruptedExceptione){e.printStackTrace();}}}

重量级锁特点:

  • 使用操作系统的互斥量(Mutex)
  • 线程会进入阻塞状态
  • 适用于高竞争场景

实战中的最佳实践

1. 锁粒度控制

在商城项目中,库存管理需要特别注意锁的粒度:

publicclassInventoryManager{// 不好的做法:锁粒度太粗privatefinalObjectglobalLock=newObject();privateMap<String,Integer>inventory=newConcurrentHashMap<>();// 好的做法:细粒度锁privatefinalMap<String,Object>productLocks=newConcurrentHashMap<>();publicvoidupdateStock(StringproductId,intquantity){// 获取商品特定的锁ObjectproductLock=productLocks.computeIfAbsent(productId,k->newObject());synchronized(productLock){IntegercurrentStock=inventory.getOrDefault(productId,0);inventory.put(productId,currentStock+quantity);}}publicbooleanpurchase(StringproductId,intquantity){ObjectproductLock=productLocks.computeIfAbsent(productId,k->newObject());synchronized(productLock){IntegercurrentStock=inventory.get(productId);if(currentStock==null||currentStock<quantity){returnfalse;}inventory.put(productId,currentStock-quantity);returntrue;}}}

2. 避免死锁

在营销系统的奖品发放中,要特别注意避免死锁:

publicclassPrizeDistribution{privatefinalObjectprizeLock=newObject();privatefinalObjectuserLock=newObject();// 错误的做法:可能产生死锁publicvoiddistributePrizeWrong(longuserId,StringprizeId){synchronized(prizeLock){synchronized(userLock){// 处理奖品发放}}}// 正确的做法:固定锁顺序publicvoiddistributePrizeRight(longuserId,StringprizeId){// 按照固定顺序获取锁ObjectfirstLock,secondLock;if(System.identityHashCode(prizeLock)<System.identityHashCode(userLock)){firstLock=prizeLock;secondLock=userLock;}else{firstLock=userLock;secondLock=prizeLock;}synchronized(firstLock){synchronized(secondLock){// 安全的奖品发放逻辑System.out.println("为用户"+userId+"发放奖品"+prizeId);}}}}

3. 双检锁单例模式

在项目配置管理中,单例模式经常使用:

publicclassConfigManager{// volatile保证可见性和禁止指令重排序privatestaticvolatileConfigManagerinstance;privateConfigManager(){// 私有构造函数}publicstaticConfigManagergetInstance(){if(instance==null){// 第一次检查synchronized(ConfigManager.class){if(instance==null){// 第二次检查instance=newConfigManager();}}}returninstance;}}

为什么需要volatile:

  • 防止指令重排序
  • 保证多线程环境下的可见性
  • 避免其他线程看到未完全初始化的对象

性能优化建议

1. 减少锁持有时间

publicclassOptimizedOrderProcessor{privateMap<String,BigDecimal>prices=newHashMap<>();privateMap<String,Integer>stock=newHashMap<>();// 优化前:锁持有时间过长publicBigDecimalcalculateTotalBad(List<String>products){synchronized(this){BigDecimaltotal=BigDecimal.ZERO;for(Stringproduct:products){// 模拟耗时操作try{Thread.sleep(10);}catch(InterruptedExceptione){e.printStackTrace();}total=total.add(prices.getOrDefault(product,BigDecimal.ZERO));}returntotal;}}// 优化后:只锁必要的部分publicBigDecimalcalculateTotalGood(List<String>products){// 先收集需要的数据(不需要同步)List<BigDecimal>priceList=newArrayList<>();for(Stringproduct:products){// 模拟耗时操作try{Thread.sleep(10);}catch(InterruptedExceptione){e.printStackTrace();}}// 同步计算总和synchronized(this){BigDecimaltotal=BigDecimal.ZERO;for(Stringproduct:products){total=total.add(prices.getOrDefault(product,BigDecimal.ZERO));}returntotal;}}}

2. 使用读写锁替代

对于读多写少的场景,考虑使用ReentrantReadWriteLock

publicclassProductCache{privatefinalMap<String,Product>cache=newHashMap<>();privatefinalReentrantReadWriteLockrwLock=newReentrantReadWriteLock();publicProductgetProduct(Stringid){rwLock.readLock().lock();// 获取读锁try{returncache.get(id);}finally{rwLock.readLock().unlock();}}publicvoidupdateProduct(Productproduct){rwLock.writeLock().lock();// 获取写锁try{cache.put(product.getId(),product);}finally{rwLock.writeLock().unlock();}}}

常见问题与解决方案

1. synchronized与Lock的区别

特性synchronizedReentrantLock
实现机制JVM层面实现JDK层面实现
锁获取自动获取释放手动获取释放
可中断不支持支持
公平锁非公平可选公平/非公平
条件变量有限支持灵活支持

2. 如何选择锁策略

根据实际场景选择合适的同步机制:

publicclassLockStrategySelector{/** * 根据场景选择锁策略 * @param scenario 场景描述 * @return 建议的锁策略 */publicStringselectLockStrategy(Stringscenario){switch(scenario){case"简单同步":return"使用synchronized,简单可靠";case"需要超时":return"使用ReentrantLock.tryLock()";case"读写分离":return"使用ReentrantReadWriteLock";case"高并发统计":return"考虑使用LongAdder";case"分布式环境":return"使用分布式锁如Redis锁";default:return"使用synchronized";}}}

总结

synchronizedJava内置的同步机制,从最初的重量级锁发展到现在的智能锁升级,性能已经得到了极大的优化。在实际项目中,需要根据具体场景选择合适的同步策略:对于简单的同步需求,synchronized是选择;对于复杂的并发控制,可以考虑ReentrantLock等更灵活的机制。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/13 14:21:35

YOLO-v5与TensorRT训练部署全流程指南

YOLO-v5与TensorRT训练部署全流程指南 在工业视觉、智能安防和自动驾驶等领域&#xff0c;实时目标检测早已不再是“有没有”的问题&#xff0c;而是“快不快、准不准、稳不稳”的工程较量。YOLO系列凭借其端到端的简洁架构和卓越的速度-精度平衡&#xff0c;成为无数落地项目的…

作者头像 李华
网站建设 2026/3/15 9:04:18

3DS格式3DS游戏全集1861个

3DS格式3DS官方游戏全集1861个&#xff0c;做好目录打包https://pan.quark.cn/s/d9e5562e6722alex91大神分享的顶置资源顶置的失效了&#xff0c;看到大神22年以后就没登录论坛&#xff0c;下面很多人评论希望补档&#xff0c;重新传一个&#xff0c;薪火相传。

作者头像 李华
网站建设 2026/3/13 12:23:26

IAG与Adobe合作,通过个性化加速增长

IAG将部署Adobe Experience Cloud&#xff0c;以提供更加个性化和无缝的客户体验。实时数据和人工智能将使IAG能够预测客户需求并深化参与度。基于该公司对其零售企业平台的投资&#xff0c;此次合作将加速创新&#xff0c;提高运营灵活性&#xff0c;并支持IAG的增长战略。202…

作者头像 李华
网站建设 2026/3/5 10:59:02

Xshell背景透明怎么办?

在使用Xshell进行远程操作时&#xff0c;不少用户会追求界面美观和使用舒适度&#xff0c;尤其在多任务切换或编写长时间脚本的时候&#xff0c;一个清晰、舒服的终端界面显得尤为重要。常见的问题之一就是&#xff1a;Xshell背景透明怎么办&#xff1f;又或者&#xff0c;Xshe…

作者头像 李华
网站建设 2026/3/16 11:57:28

LobeChat能否对接Jira?敏捷开发团队福音

LobeChat能否对接Jira&#xff1f;敏捷开发团队福音 在今天的软件研发环境中&#xff0c;一个开发者平均每天要切换6到8个系统&#xff1a;从代码仓库、CI/CD流水线&#xff0c;到项目管理工具和即时通讯平台。这种高频的上下文切换不仅消耗注意力&#xff0c;还极易导致信息遗…

作者头像 李华
网站建设 2026/3/14 12:56:43

GPT-OSS本地部署指南:Ollama+MoE实战

GPT-OSS本地部署指南&#xff1a;OllamaMoE实战 在 AI 技术快速普及的今天&#xff0c;一个现实问题摆在开发者面前&#xff1a;如何在不依赖云服务、不牺牲性能的前提下&#xff0c;将接近 GPT-4 水平的大模型真正“握在手中”&#xff1f;答案正在浮现——GPT-OSS-20B&#x…

作者头像 李华