【JUC第二章上】:锁机制&关键字
本文详细解析了Java中synchronized锁的底层原理与升级机制。主要内容包括:1)锁升级流程(无锁→偏向锁→轻量级锁→重量级锁),重点分析了JDK15前后不同的升级路径;2)三种锁的核心原理与适用场景:偏向锁适合单线程无竞争场景,轻量级锁采用CAS+自旋适合轻度竞争,重量级锁依赖操作系统互斥量适合高竞争场景;3)锁升级的核心思想是根据竞争程度自动选择最优锁机制,平衡性能与开销。文章还深入探
🔥你好我是fengxin_rou这是我的个人主页fengxin_rou的主页
❄️欢迎查看我的专栏我的专栏
《Java后端学习》、《JAVASE基础》、《JUC并发》、《redis》、《JVM虚拟机》、《MYSQL》、《黑马点评》、《rabbitmq》、《JavaWeb+AI的talis学习系统》、《苍穹外卖》

目录
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 等操作的 “安全执行环境”
轻量级锁(自旋锁,短时间、轻度竞争)
偏向锁撤销后,如果线程在同步块内,会进入轻量级锁阶段:
- 线程在自己栈帧中创建锁记录(Lock Record)。
- 将对象头的
Mark Word复制到锁记录中。 - 使用 CAS 操作 尝试将对象头的
Mark Word替换为指向锁记录的指针。 - 如果 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 + 自旋尝试获取锁,不阻塞。 场景:线程交替访问,锁执行时间短。
重量级锁
原理:依赖操作系统互斥量,线程阻塞排队。 场景:高竞争、锁执行时间长。

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


所有评论(0)