【JUC】一文吃透 synchronized 底层原理:从基础使用到 Monitor 锁机制
目录
二、synchronized 底层核心:Monitor 锁机制
4. 底层指令:monitorenter 与 monitorexit
在 Java 多线程开发中,synchronized 是解决线程安全问题最基础、最核心的关键字,它能保证代码的原子性、可见性、有序性,是面试和实际开发中绕不开的重点。本文从基础使用入手,深入讲解其底层 Monitor 锁实现原理,帮你彻底理解 synchronized。
一、synchronized 基础使用
synchronized 本质是互斥锁,同一时间只允许一个线程执行被修饰的代码,它有三种使用场景:
1. 修饰实例方法
锁的是当前实例对象,同一个对象的多线程调用会互斥,不同对象互不影响。
public class SyncDemo {
// 锁当前实例对象
public synchronized void method() {
// 同步代码逻辑
}
}
注意注意:synchronized 修饰实例方法时,锁的是调用该方法的具体对象实例(this),而非方法本身。
也就是说,SyncDemo 的一个实例对象同一时刻只能有一个线程进行调用,这里给出一个样例看一下
public class SyncDemo {
// 计数器,用于直观展示执行顺序
private int count = 0;
// synchronized修饰实例方法,锁的是调用该方法的SyncDemo实例对象
public synchronized void method() {
// 模拟耗时操作,让线程执行时间更明显
for (int i = 0; i < 3; i++) {
count++;
System.out.println(Thread.currentThread().getName() +
" - count = " + count);
try {
Thread.sleep(500); // 暂停500毫秒,放大执行差异
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public static void main(String[] args) {
// ========== 场景1:多线程调用【同一个对象】的同步方法 ==========
System.out.println("===== 场景1:同一对象,多线程互斥 =====");
SyncDemo sameObject = new SyncDemo(); // 唯一的对象实例
// 线程1调用sameObject的method方法
Thread thread1 = new Thread(() -> sameObject.method(), "线程1");
// 线程2调用sameObject的method方法
Thread thread2 = new Thread(() -> sameObject.method(), "线程2");
thread1.start();
thread2.start();
// 等待场景1的线程执行完毕,再执行场景2
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// ========== 场景2:多线程调用【不同对象】的同步方法 ==========
System.out.println("\n===== 场景2:不同对象,互不影响 =====");
SyncDemo object1 = new SyncDemo(); // 第一个对象
SyncDemo object2 = new SyncDemo(); // 第二个对象
// 线程3调用object1的method方法
Thread thread3 = new Thread(() -> object1.method(), "线程3");
// 线程4调用object2的method方法
Thread thread4 = new Thread(() -> object2.method(), "线程4");
thread3.start();
thread4.start();
}
}

2. 修饰静态方法
锁的是当前类的 Class 对象,全局唯一,所有线程调用该方法都会互斥。
public class SyncDemo {
// 锁当前类的Class对象
public static synchronized void staticMethod() {
// 同步代码逻辑
}
}
有了上面的例子,其实这个很好理解了,就是所有的实例对象同一时刻只能有一个线程调用
3. 修饰代码块
灵活指定锁对象,粒度更细,推荐优先使用。
public class SyncDemo {
private final Object lock = new Object();
public void test() {
// 锁指定对象
synchronized (lock) {
// 同步代码逻辑
}
}
}
说白了synchronized 锁的本质是锁对象,而非代码,不同锁对象互不干扰。
二、synchronized 底层核心:Monitor 锁机制
synchronized 之所以能实现互斥,底层依赖 JVM 的 Monitor(监视器锁) 机制,而 Monitor 又和 Java 对象头密切相关。
1. 前置知识:Java 对象头
Java 对象在内存中分为三部分:对象头、实例数据、对齐填充,其中对象头是实现 synchronized 的关键。

对象头主要包含:
- Mark Word:存储对象的哈希码、分代年龄、锁状态标志、线程持有的锁、偏向线程 ID 等核心信息。
- Klass Pointer:指向对象对应类的元数据指针。
锁状态就存在 Mark Word 中,synchronized 就是通过修改 Mark Word 实现锁升级(本文重点讲重量级锁对应的 Monitor 机制)。
重点来看一下 Mark Word

hashcode:25位的对象标识Hash码
age:对象分代年龄占4位
biased_lock:偏向锁标识,占1位 ,0表示没有开始偏向锁,1表示开启了偏向锁
thread:持有偏向锁的线程ID,占23位
epoch:偏向时间戳,占2位
ptr_to_lock_record:轻量级锁状态下,指向栈中锁记录的指针,占30位
ptr_to_heavyweight_monitor:重量级锁状态下,指向对象监视器Monitor的指针,占30位
上面标红的则为锁升级时重要使用的标志位
我们可以通过lock的标识,来判断是哪一种锁的等级
后三位是001表示无锁
后三位是101表示偏向锁
后两位是00表示轻量级锁
后两位是10表示重量级锁
2. 什么是 Monitor?
Monitor 直译是监视器,JVM 中它是一个同步工具 / 同步原语,本质是一个对象监视器锁,每个 Java 对象都关联一个 Monitor,底层由C++语言实现
可以把 Monitor 理解为一个房间,同一时间只能有一个线程进入:
- 进入房间 = 加锁
- 离开房间 = 释放锁
- 没抢到锁的线程 = 在门外等待
Monitor 核心数据结构如下:

核心包含三个关键队列 / 指针:
- _owner:指向当前持有锁的线程,未持有锁时为 null。
- _EntryList:锁等待队列,所有竞争锁失败的线程会进入这里,处于阻塞状态。
- _WaitSet:等待队列,调用
wait()方法的线程会进入这里,释放锁,被notify()/notifyAll()唤醒后重新竞争锁。
3. synchronized 加锁 / 解锁完整流程
以 synchronized(object) 代码块为例,底层执行流程:
- 线程进入同步代码块,先尝试获取对象的 Monitor 锁。
- 检查
_owner是否为 null:- 为 null:表示锁未被持有,当前线程将
_owner指向自己,加锁成功,执行同步代码。 - 不为 null:表示锁已被其他线程持有,当前线程进入
_EntryList阻塞,直到锁被释放。
- 为 null:表示锁未被持有,当前线程将
- 持有锁的线程执行完同步代码,或调用
wait()方法:- 正常执行完毕:释放锁,
_owner置为 null,唤醒_EntryList中的线程重新竞争。 - 调用
wait():释放锁,线程进入_WaitSet,等待被唤醒。
- 正常执行完毕:释放锁,
- 线程被
notify()唤醒后,从_WaitSet移到_EntryList,重新参与锁竞争。
注意注意:参与锁竞争只能是_EntryList里面的线程或者是新来的线程,在 _WaitSet里的线程必须先被唤醒进入阻塞队列
4. 底层指令:monitorenter 与 monitorexit
JVM 为 synchronized 提供了两个字节码指令:
- monitorenter:进入同步代码块时执行,尝试获取对象的 Monitor 锁。
- monitorexit:退出同步代码块时执行,释放 Monitor 锁。
借助javap命令查看class的字节码,比如以下代码:
public class SyncTest {
static final Object lock = new Object();
static int counter = 0;
public static void main(String[] args) {
synchronized (lock) {
counter++;
}
}
}
找到这个类的class文件,在class文件目录下执行javap -v SyncTest.class,反编译效果如下:

注意:JVM 会保证异常也能释放锁,所以代码中会有一个 monitorenter 对应两个 monitorexit(一个正常退出,一个异常退出)。
三、核心知识点总结
- synchronized 是互斥锁,保证多线程下的线程安全,实现原子性、可见性、有序性。
- 锁的是对象,实例方法锁当前对象,静态方法锁 Class 对象,代码块锁指定对象。
- 底层依赖 Monitor 监视器锁,通过对象头 Mark Word 关联。
- Monitor 核心:
_owner(持有锁线程)、_EntryList(锁等待)、_WaitSet(wait 等待)。 - 底层字节码:
monitorenter加锁,monitorexit解锁,异常安全释放锁。
本文只讲解了 synchronized 基础使用和最核心的 Monitor 重量级锁原理,后续还有偏向锁、轻量级锁的锁升级机制,是 synchronized 优化的关键,下篇文章继续深入讲解。
感兴趣的宝子可以关注一波,后续会更新更多有用的知识!!!

更多推荐




所有评论(0)