当前位置: 首页 > ai >正文

Java 的 synchronized

synchronized 是 java 内置的关键字,用于多线程的同步操作。

synchronized 修饰在方法或代码块上时,会对特定的对象或类加锁,从而确保同一时刻只有一个线程能执行加锁的代码块。

  • synchronized 修饰方法:会在方法的访问标志中增加一个 ACC_SYNCHRONIZED 标志。每当一个线程访问该方法时,JVM 会检查方法的访问标志。如果包含 ACC_SYNCHRONIZED 标志,线程必须先获得该方法对应的对象的监视器锁(即对象锁),然后才能执行该方法,从而保证方法的同步性。
  • synchronized 修饰代码块:会在代码块的前后插入 monitorentermonitorexit 字节码指令。可以把 monitorenter 理解为加锁,monitorexit理解为解锁。

synchronized 无法完全禁止指令重排序,但能通过内存屏障保证多线程环境下的有序性。对于需要严格禁止重排序的场景,应优先选择 volatile。


实现原理

通过 JVM 的 Monitor(监视器锁) 和 对象头(Object Header)。

对象头(Object Header)
  • mark word:记录对象的哈希码、分代年龄、锁标志位等。
  • klass pointer:指向对象类型的元信息

64 位时 MarkWord 在不同状态下的内存布局

在这里插入图片描述
Mark Word 是实现 synchronized 的关键,因为它会根据锁的状态保存不同的信息,具体包括:

  • 未锁定状态:Mark Word 存储对象的哈希码和 GC 分代信息。
  • 偏向锁状态:Mark Word 保存获取该锁的线程 ID 和一些偏向锁标志位。
  • 轻量级锁状态:Mark Word 存储的是指向栈中锁记录的指针。
  • 重量级锁状态:Mark Word 存储的是指向 Monitor 对象的指针。
对象结构图

锁升级过程

synchronized 的锁升级过:无锁–>偏向锁–>轻量级锁–>重量级锁。

偏性锁
  • 加锁过程:当有一个线程去获取锁时,jvm 会将该线程的 id 记录在对象头 mark word 中,等线程再次获锁时,jvm 无须在进行加锁操作。
  • 撤销偏向锁:如果在偏向锁持有期间,另一个线程请求同一把锁,jvm 会撤销偏向锁并升级为轻量级锁。
轻量级锁
  • 加锁过程:当线程获取锁时,jvm 会在当前线程的栈帧中创建一个锁记录,并将对象头中的 mark word 复制到锁记录中,然后利用 CAS 将对象头的 mark word 替换为指向锁记录的指针, 如果成功,则该线程获得轻量级锁,如果失败,表示其他线程已经持有该锁,此时会升级为重量级锁。
  • 解锁过程:线程退出同步块时,JVM 会将对象头中的 Mark Word 恢复为原始值。

轻量级锁 CAS 失败了之后,不会有自旋操作,会直接进入重量级锁膨胀过程。在已经升级成重量级锁之后,线程如果没有争抢到锁,会进行一段自旋等待锁的释放。

重量级锁

当锁竞争激烈时,JVM 会升级为重量级锁,重量级锁使用操作系统的互斥量(Mutex) 机制来实现线程的阻塞与唤醒。

  • 加锁过程:如果线程无法通过轻量级锁获取锁时,则会升级为重量级锁。
  • 解锁过程:当线程释放重量级锁时,jvm 会唤醒所有阻塞的线程,允许他们再次获取锁。

当重量级锁释放了之后,锁对象是无锁的。有新的线程来竞争的话又会从无锁再到轻量级锁开始后续的升级流程。

锁升级过程总结

偏性锁:当线程第一次获取锁时,jvm 会将当前线程的 id 记录在对象头中,后续该线程再次获取锁时,几乎没有开销。

轻量级锁:当一个线程去获取已经被偏性的锁时,此时锁会升级为轻量级锁。

重量级锁:如果 CAS 失败无法获取锁时,此时锁会升级为重量级锁,线程会被挂起,直到锁被释放。

Synchronized 的可重入性

synchronized 是可重入的,每获取一次锁,计数器加一,释放锁时,计数器减一,直到计数器为 0,锁才会真正释放。

锁消除和锁粗化

  • 锁消除:JVM 会通过逃逸分析判断对象是否只在当前线程使用,如果是,那么会消除不必要的加锁操作。
  • 锁粗化:当多个锁操作频繁出现时,JVM 会将这些锁操作合并,减少锁获取和释放的开销。

修饰静态方法和修饰普通方法的区别

  • 修饰静态方法:锁的是这个类的 Class 对象。也就是说,无论创建了多少个该类的实例,所有的实例共享同一个锁,因为这个锁属于类本身而不是某个对象实例。
  • 修饰实例方法:锁的是当前实例(调用该方法的对象),也就是这个对象的内在锁。这也就是说每个对象实例都有自己独立的锁。
http://www.xdnf.cn/news/8735.html

相关文章:

  • 【计算机网络】基于TCP进行socket编程——实现客户端到服务端远程命令行操作
  • Linux Kernel调试:强大的printk(二)
  • git子模块--命令--表格版
  • Spring MVC 的的核心原理与实践指南
  • 记录第一次正式收到SCI期刊论文的审稿
  • Github 2025-05-25 php开源项目日报 Top10
  • 进阶-自定义类型(结构体、位段、枚举、联合)
  • 在 Ubuntu linux系统中设置时区的方案
  • C++引用以及和指针的区别
  • PyTorch Image Models (timm) 技术指南
  • 【Linux】进程信号(一):信号的产生与信号的保存
  • 刷题记录(6)栈与队列相关操作
  • 独占内存访问工作原理
  • # 探索自然语言处理的奥秘:基于 Qwen 模型的文本分类与对话系统实现
  • 【HW系列】—web常规漏洞(SQL注入与XSS)
  • 算法笔记·数学·扩展欧几里得算法
  • 【MySQL】事务
  • 大学之大:浦项科技大学2025.5.25
  • 针对vue项目的webpack优化攻略
  • Kotlin IR编译器插件开发指南
  • 保留字、变量与常量
  • 数智管理学(十一)
  • 人工智能赋能教育:重塑学习生态,开启智慧未来
  • QListWidget的函数,信号介绍
  • 华为OD机试真题—— 矩阵中非1的数量 (2025B卷:200分)Java/python/JavaScript/C/C++/GO最佳实现
  • 深入解析Spring Boot与JUnit 5集成测试的最佳实践
  • 【论文阅读】KIMI-VL TECHNICAL REPORT
  • window 显示驱动开发-Direct3D 呈现性能改进(四)
  • 刚入门3DGS的新手小白能够做的工作
  • 项目阅读:Instruction Defense