synchronized 原理

简介: 本文详解 Java 中 `synchronized` 的底层实现原理及锁升级机制。通过 Monitor 对象管理线程竞争,涉及 owner、EntryList、WaitSet 等结构,并介绍偏向锁、轻量级锁、重量级锁的升级过程。同时对比 `synchronized` 与 `volatile`、`Lock` 的区别,涵盖原子性、可见性、有序性及功能扩展性,帮助理解并发编程中的线程安全机制。

synchronized 底层原理

以重量级锁为例,比如 T0、T1 两个线程同时执行加锁代码,已经出现了竞争(代码如下)

synchronized(obj) { // 加锁
    ...
} // 解锁
  1. 当执行到行1 的代码时,会根据 obj 的对象头找到或创建此对象对应的 Monitor 对象(C++对象)
  • Monitor的核心结构包含:
  • owner:指向持有该Monitor的线程
  • WaitSet:存放出于wait状态的线程队列
  • EntryList:存放出于阻塞状态的线程队列
  • recursions:记录线程重入次数
  1. 检查 Monitor 对象的 owner 属性,用 Cas 操作去设置 owner 为当前线程,Cas 是原子操作,只能有一个线程能成功
  1. 假设 T0 Cas 成功,那么 T0 就加锁成功,将 recursions 置为1,若线程已持有该Monitor(可重入),则        recursions +1,然后T0线程继续执行 synchronized 代码块内的部分
  2. T1 这边 Cas 失败,会自旋若干次,重新尝试加锁,如果
  1. 重试过程中 T0 释放了锁,则 T1 不必阻塞,加锁成功
  2. 重试时 T0 仍持有锁,则 T1 会进入 Monitor 的等待队列EntryList,将来 T0 解锁后会唤醒它恢复运行(去重新抢锁)


synchronized 锁升级


synchronized 锁有三个级别:偏向锁、轻量级锁、重量级锁,性能从左到右逐渐降低

  • 如果就一个线程对同一对象加锁,此时就用偏向锁
  • 又来一个线程,与前一个线程交替为对象加锁,但只是交替,没有竞争,此时要升级为轻量级锁
  • 如果多个线程加锁时发生了竞争,必须升级为重量级锁

【说明】

  • 自 java 6 开始对 synchronized 提供了锁升级功能,之前只有重量级锁
  • 但从 java 15 开始,偏向锁被标记为已废弃,将来会移除(因为实际带来的性能提升不明显,某些情况下反而影响性能)


对比 synchronized 和 volatile


并发编程需要从三个方面考虑线程安全,分别是:原子性、可见性、有序性

  • volatile 修饰共享变量,可以保证它的可见性和有序性,但不能保证原子性(JMM模型)
  • synchronized 代码块,不仅能保证共享变量的可见性、有序性,同时也能保证原子性


对比 synchronized 和 Lock


  • synchronized 是关键字,Lock 是 Java 接口
  • 前者底层是 C++ 代码实现锁,后者是 Java 自己的代码来实现锁
  • Lock 功能更多,比如可以选择是公平锁还是非公平锁、可以设置加锁超时时间、可打断等
  • Lock 的提供多种扩展实现(例如读写锁),可以根据场景选择更合适的实现
  • Lock 释放锁需要调用 unlock 方法,而 synchronzied 在代码块结束无需显式调用就可以释放锁
相关文章
|
XML Java 数据格式
Spring-实例化bean的四种方式
Spring-实例化bean的四种方式
838 0
|
5月前
|
编解码 Windows
新写了个直播录制工具,可录制抖音快手斗鱼直播
新推直播录制工具M3U8Quicker,基于原视频下载工具升级,支持抖音、快手、斗鱼等主流平台的HTTP-FLV/HLS直播录制。安装浏览器扩展后自动识别直播流,一键新建任务、持续录制至下播,支持转码H.265→H.264。开源免费,GitHub可下载。
1252 0
|
机器学习/深度学习 人工智能 自然语言处理
ai人工智能课程学什么
本内容全面介绍了AI课程的核心体系,涵盖基础理论、核心算法、应用领域及伦理责任等方面。从数学基础与编程技能到机器学习和深度学习算法,再到自然语言处理与计算机视觉等应用领域,系统阐述了AI技术的全貌。同时探讨了开发框架如TensorFlow和PyTorch的使用,并关注AI伦理与社会责任。通过分步验证与实践经验,帮助学习者规避AI局限性。展望未来,生成式人工智能等新兴技术将持续推动课程发展,助力职业成长与社会进步。
|
10月前
|
人工智能 运维 前端开发
Skill菜单全是英文记不住?改一行配置就行!顺带懒人一键提示词!
本文介绍如何通过修改Claude Code中Skill的`name`和`description`字段(在`SKILL.md`文件内),为英文Skill名添加简明中文说明,实现菜单可视化、智能排序与快速识别。操作简单,一行配置即生效,大幅提升AI编程效率。(239字)
|
Linux
linux中查看某个文件夹下文件的个数和大小
这篇文章介绍了在Linux系统中使用各种命令(如`stat`、`wc`、`du`和`ls`)来查看文件夹下文件的个数和大小的方法。
4897 5
linux中查看某个文件夹下文件的个数和大小
|
存储 缓存 人工智能
【原理】【Java并发】【synchronized】适合中学者体质的synchronized原理
本文深入解析了Java中`synchronized`关键字的底层原理,从代码块与方法修饰的区别到锁升级机制,内容详尽。通过`monitorenter`和`monitorexit`指令,阐述了`synchronized`实现原子性、有序性和可见性的原理。同时,详细分析了锁升级流程:无锁 → 偏向锁 → 轻量级锁 → 重量级锁,结合对象头`MarkWord`的变化,揭示JVM优化锁性能的策略。此外,还探讨了Monitor的内部结构及线程竞争锁的过程,并介绍了锁消除与锁粗化等优化手段。最后,结合实际案例,帮助读者全面理解`synchronized`在并发编程中的作用与细节。
1191 8
【原理】【Java并发】【synchronized】适合中学者体质的synchronized原理
|
安全 Java 程序员
Java面试必问!run() 和 start() 方法到底有啥区别?
在多线程编程中,run和 start方法常常让开发者感到困惑。为什么调用 start 才能启动线程,而直接调用 run只是普通方法调用?这篇文章将通过一个简单的例子,详细解析这两者的区别,帮助你在面试中脱颖而出,理解多线程背后的机制和原理。
1269 12
|
安全 Java API
【Java性能优化】Map.merge()方法:告别繁琐判空,3行代码搞定统计累加!
在日常开发中,我们经常需要对Map中的值进行累加统计。}else{代码冗长,重复调用get()方法需要显式处理null值非原子操作,多线程下不安全今天要介绍的方法,可以让你用一行代码优雅解决所有这些问题!方法的基本用法和优势与传统写法的对比分析多线程安全版本的实现Stream API的终极优化方案底层实现原理和性能优化建议一句话总结是Java 8为我们提供的Map操作利器,能让你的统计代码更简洁、更安全、更高效!// 合并两个列表});简单累加。
1289 0
|
存储 安全 Java
面试高频:Synchronized 原理,建议收藏备用 !
本文详解Synchronized原理,包括其作用、使用方式、底层实现及锁升级机制。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
面试高频:Synchronized 原理,建议收藏备用 !