🔥你好我是fengxin_rou这是我的个人主页fengxin_rou的主页

❄️欢迎查看我的专栏我的专栏

《Java后端学习》《JAVASE基础》《JUC并发》《redis》《JVM虚拟机》《MYSQL》《黑马点评》《rabbitmq》《JavaWeb+AI的talis学习系统》《苍穹外卖》

目录

synchronized 底层原理?

synchronized 锁升级流程:

无锁:

偏向锁:

2. 偏向锁(单线程无竞争)

偏向锁撤销

3. 轻量级锁(自旋锁,短时间、轻度竞争)

轻量级锁竞争

自适应自旋(JDK 1.6+)

4. 重量级锁(高竞争、长持有)

最终锁升级路径(标准总结)

JDK 15 以前(经典路径)

JDK 15+(现代 JVM,已废弃偏向锁)

锁升级核心思想

偏向锁、轻量级锁、重量级锁各自原理和适用场景?

1. 偏向锁(Biased Lock)

原理

特点

适用场景

2. 轻量级锁(Lightweight Lock)

原理

特点

适用场景

3. 重量级锁(Heavyweight Lock)

原理

特点

适用场景

总结

偏向锁

轻量级锁

重量级锁


synchronized 底层原理?


synchronized是Java提供的原子性内置锁,这种内置的并且使用者看不到的锁也被称为监视器锁

一个同步块里只有一个锁对象,然后里面的线程都想要使用这个对象,想要使用的都执行monitorenter命令都在enterList里面争抢锁,成功的就计数器加1,失败的就继续在enterList里面等待,等锁释放了就monitorexist计数器-1,当线程执行wait方法释放锁后会进入waitList队列

synchronized 锁升级流程:

无锁→偏向锁→轻量级锁→重量级锁

这是一个单向性的过程不能降级

这个机制的核心在于对象头中的Mark Word,它会根据锁状态存储不同的信息,通过其中的锁标志位来标识当前锁处于什么状态。

无锁:
 

当一个对象刚创建出来的时候,没有线程访问他,并且mark word里面存储的是他的hashcode、分代年龄等信息,锁标志位是0、1,偏向锁位是0,如果使用了hashcode方法,就不能升级到偏向锁,因为偏向锁需要用Mark word的空间来存储当前线程ID,hashcode也要占用这一部分,冲突了

偏向锁:


意思就是第一个线程进入同步块时,这个同步块的锁升级为偏向锁,把锁的偏向位置位为1,标位置还是01,并且通过CAS操作记录当前进入线程的id,如果当这一个线程来访问当同步块时,只需要检验是否是记录的id,如果是就可以直接进入,不是就说明是其他线程,这里就需要撤销偏向锁,然后等待全局安全点,暂停持有偏向锁的线程,清除他的偏向标记,并检查是否在同步块内,如果在就升级为轻量锁,不在就恢复无锁

无锁状态(01,偏向0)
        ↓ 第一个线程进入同步块
偏向锁状态(01,偏向1,记录线程ID)
        ↓ 同一线程再次进入
直接进入(零开销)
        ↓ 其他线程尝试获取
撤销偏向锁 → 等待安全点 → 暂停原线程 → 检查状态
        ↓
原线程已退出? → 恢复无锁状态
原线程未退出? → 升级为轻量级锁(00)

JDK 15+ 默认禁用偏向锁,JDK 18+ 彻底废弃,现代 JVM 锁升级路径: 无锁 → 轻量级锁 → 重量级锁

全局安全点是 JVM 中一个所有 Java 线程都到达的、可以安全暂停执行的状态点,是偏向锁撤销、GC 等操作的 “安全执行环境”


轻量级锁(自旋锁,短时间、轻度竞争)

偏向锁撤销后,如果线程在同步块内,会进入轻量级锁阶段:

  1. 线程在自己栈帧中创建锁记录(Lock Record)
  2. 将对象头的 Mark Word 复制到锁记录中。
  3. 使用 CAS 操作 尝试将对象头的 Mark Word 替换为指向锁记录的指针
  4. 如果 CAS 成功,当前线程获取轻量级锁,锁标志位变为 00

注意:轻量级锁升级过程在撤销偏离锁的时候就决定了,这里只是获得锁

轻量级锁竞争

  • 如果 CAS 失败,说明有其他线程竞争。
  • 当前线程开始自旋,循环重试 CAS 尝试获取锁。
  • 自旋是用 CPU 时间换避免线程阻塞的开销,适合锁执行时间很短的场景。

自适应自旋(JDK 1.6+)

在JDK1.6之前自旋次数固定是10次,可以通过参数调整。JDK1.6之后引入了自适应自旋,JVM会根据上次在这个锁上的自旋情况动态调整自旋次数

  • JVM 会根据历史自旋成功率动态调整自旋次数
  • 上一个线程自旋成功 → 当前线程自旋更久;上一个线程自旋失败 → 当前线程快速放弃。

4. 重量级锁(高竞争、长持有)

当自旋达到阈值仍未获取锁,或出现多线程同时竞争时: 轻量级锁膨胀为重量级锁。其实就是sychornized底层锁,流程一致

想要竞争锁的进入enterlist,然后竞争,得到的计数器就monitorenter加1,失败了就monitorexist计数器减一,使用了wait方法就释放锁,进入waitlist

  • 锁标志位变为 10
  • Mark Word 指向 Monitor(监视器锁) 对象。
  • 未获取锁的线程进入 EntryList 阻塞排队。
  • 依赖操作系统 Mutex 互斥量 实现。
  • 线程阻塞 / 唤醒需要 用户态 ↔ 内核态切换,开销最大。

优点:竞争激烈、锁持有时间长时,比自旋更节省 CPU。


最终锁升级路径(标准总结)

JDK 15 以前(经典路径)

无锁 → 偏向锁 → 轻量级锁 → 重量级锁

JDK 15+(现代 JVM,已废弃偏向锁)

无锁 → 轻量级锁 → 重量级

锁升级核心思想

根据线程竞争程度,自动选择成本最低的锁机制: 无竞争用偏向锁,轻度竞争用轻量级锁,高竞争用重量级锁。 用复杂度换性能,让 synchronized 在各种场景都高效。
 

偏向锁、轻量级锁、重量级锁各自原理和适用场景?
 

1. 偏向锁(Biased Lock)

原理

  • 锁一开始偏向第一个获取它的线程
  • 在对象头 Mark Word记录线程 ID
  • 下次这个线程再进来,不需要 CAS、不需要抢锁,直接判断 ID 是不是自己,是就直接进同步块。
  • 锁标志位:01,偏向位 = 1
  • 一旦出现第二个线程竞争,偏向锁立刻撤销,升级为轻量级锁。

特点

  • 无锁开销、最快
  • 只服务一个线程
  • 撤销偏向锁的成本很高

适用场景

只有一个线程反复进入同步块,完全无竞争。


2. 轻量级锁(Lightweight Lock)

原理

  • 线程在自己栈帧中创建锁记录(Lock Record)
  • CAS 尝试把对象头的 Mark Word 替换成指向锁记录的指针
  • CAS 成功 = 加锁成功
  • CAS 失败 = 线程开始自旋,循环重试
  • 锁标志位:00

特点

  • 不阻塞、不进入内核态
  • 自旋消耗 CPU,但避免线程切换开销
  • 竞争变激烈就膨胀为重量级锁

适用场景

多个线程交替访问同步块,轻度竞争,锁执行时间非常短。


3. 重量级锁(Heavyweight Lock)

原理

  • 依赖 操作系统 Mutex 互斥锁
  • 对象头 Mark Word 指向 Monitor
  • 抢不到锁的线程进入 EntryList 阻塞
  • 阻塞 / 唤醒需要 用户态 ↔ 内核态切换
  • 锁标志位:10

特点

  • 线程会阻塞,不消耗 CPU
  • 切换成本最高、最慢
  • 最稳定、最安全

适用场景

竞争激烈,锁执行时间长,多线程同时争抢。


总结

偏向锁

原理:锁偏向第一个线程,记录线程 ID,无竞争无需加锁。 场景:单线程重复访问,无竞争。

轻量级锁

原理:使用 CAS + 自旋尝试获取锁,不阻塞。 场景:线程交替访问,锁执行时间短。

重量级锁

原理:依赖操作系统互斥量,线程阻塞排队。 场景:高竞争、锁执行时间长。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐