
文章目录
- [一、synchronized 的作用](#一、synchronized 的作用)
-
-
- [`synchronized` 解决两个问题:](#
synchronized解决两个问题:)
- [`synchronized` 解决两个问题:](#
-
- [二、synchronized 的三种使用方式](#二、synchronized 的三种使用方式)
-
- [✔ 1. 修饰实例方法(锁 ------ 当前对象 this)](#✔ 1. 修饰实例方法(锁 —— 当前对象 this))
- [✔2. 修饰静态方法(锁 ------ 当前类的 Class 对象)](#✔2. 修饰静态方法(锁 —— 当前类的 Class 对象))
- [✔3. 修饰代码块(锁 ------ 任意对象)](#✔3. 修饰代码块(锁 —— 任意对象))
- [三、synchronized 的底层原理(Monitor 机制)](#三、synchronized 的底层原理(Monitor 机制))
-
- [✔1. synchronized 基于对象监视器(Monitor)实现](#✔1. synchronized 基于对象监视器(Monitor)实现)
- [四、锁的升级过程(JDK 6 后重要优化)](#四、锁的升级过程(JDK 6 后重要优化))
- 五、可见性是如何保证的?
- [六、synchronized 与 Reentrant(可重入性)](#六、synchronized 与 Reentrant(可重入性))
- [七、synchronized 与 volatile 的区别(面试高频)](#七、synchronized 与 volatile 的区别(面试高频))
- [八、synchronized 的优缺点](#八、synchronized 的优缺点)
- [九、synchronized VS Lock(面试必考)](#九、synchronized VS Lock(面试必考))
- 十、实战
一、synchronized 的作用
synchronized 是 Java 提供的最基础、最核心的线程同步机制,用来保证多线程环境下的互斥访问 和可见性 。
虽然简单易用,但其背后的锁原理(Monitor)、对象头(Mark Word)、锁升级(偏向锁→轻量级锁→重量级锁)等概念极其重要,也是面试高频考点。
synchronized 解决两个问题:
- 互斥性(Mutual Exclusion)
保证同一时刻只有一个线程可以执行临界区代码。 - 可见性(Visibility)
保证释放锁前对共享变量的修改对下一个获得锁的线程可见(类似 volatile 的效果)。
二、synchronized 的三种使用方式
✔ 1. 修饰实例方法(锁 ------ 当前对象 this)
java
public synchronized void test() {
// 临界区
}
等价于:
java
public void test() {
synchronized (this) {
// 临界区
}
}
特点:
- 作用于:同一个对象实例
- 多个线程访问不同对象时不会互斥
✔2. 修饰静态方法(锁 ------ 当前类的 Class 对象)
java
public static synchronized void test() {
// 临界区
}
等价于:
java
synchronized (MyClass.class) {
// 临界区
}
特点:
- 锁住的是 类对象,所有实例共享一把锁
✔3. 修饰代码块(锁 ------ 任意对象)
java
Object lock = new Object();
synchronized (lock) {
// 临界区
}
特点:
- 最灵活
- 推荐在实际开发中优先使用
- 可以对任何对象加锁(Object、String 等)
三、synchronized 的底层原理(Monitor 机制)
✔1. synchronized 基于对象监视器(Monitor)实现
Java 中,每一个对象都可以作为锁,因为:
- 每个对象在 JVM 中都有一个 对象头(Object Header)
- 对象头中包含锁状态(Mark Word)
- synchronized 通过 Monitor(监视器锁)实现互斥
四、锁的升级过程(JDK 6 后重要优化)
Java 的锁不是一开始就用重量级锁,而是逐步升级,以提升性能。
| 锁类型 | 特点 | 适用场景 |
|---|---|---|
| 无锁 | 无竞争 | 单线程 |
| 偏向锁 | 无需 CAS,完全无竞争 | 同一线程反复进入 |
| 轻量级锁 | 使用 CAS,自旋 | 少量竞争 |
| 重量级锁 | 阻塞/唤醒,最慢 | 竞争激烈 |
⚠️注意事项:
- 锁只能从低级到高级升级
- 不会降级(重要)
五、可见性是如何保证的?
当一个线程释放锁时,JVM 会做两件事:
- 把工作内存的变量刷新到主内存(释放锁 → 写回)
- 获得锁的线程会从主内存重新读取变量
因此保证:
- 前一个线程修改的数据对后一个线程可见
六、synchronized 与 Reentrant(可重入性)
synchronized 是 可重入锁 :
同一线程可以多次获得同一把锁不会死锁。
java
public synchronized void a() {
b(); // 可以再次获得同一把锁
}
public synchronized void b() {
// ...
}
七、synchronized 与 volatile 的区别(面试高频)
| 对比 | synchronized | volatile |
|---|---|---|
| 可见性 | ✔有 | ✔有 |
| 原子性 | ✔有(加锁) | ❌没有 |
| 是否阻塞 | ✔会阻塞 | ❌不阻塞 |
| 用途 | 多个操作的原子性 | 单变量读写 |
| 底层 | Monitor、锁升级 | 内存屏障、禁止指令重排 |
volatile 解决可见性,synchronized 解决原子性 + 可见性。
八、synchronized 的优缺点
✔优点
- 语法简单
- JVM 层面保证,没有死锁风险(可重入)
- 经过大量优化(锁消除、偏向锁)性能已大幅提升
✔缺点
- 粒度不够灵活(无条件等待)
- 性能比 Lock 略弱(尤其在高并发中)
- 无法进行尝试锁 / 中断锁 / 超时锁 等操作
九、synchronized VS Lock(面试必考)
| 特性 | synchronized | Lock |
|---|---|---|
| 锁的释放 | 自动释放 | 必须 unlock() |
| 可中断 | ❌ | ✔ |
| 可尝试 | ❌ | ✔(tryLock) |
| 公平锁 | ❌ | ✔ |
| 条件队列 | ❌ | ✔(Condition) |
| 性能 | 被优化后差别不大 | 更灵活,高并发优势明显 |
十、实战
建议 1:优先使用 synchronized(JDK 8 性能已非常好)
建议 2:优先锁代码块,而不是锁整个方法
建议 3:锁对象要私有,避免被外部使用
java
private final Object lock = new Object();
建议 4:避免锁住字符串常量(共享跨 ClassLoader)
java
synchronized ("LOCK") {} // ❌ 千万不要这样写
参考资料