好的,我们来做一个 Java synchronized 关键字的全面解析,从基础用法到底层原理,让你既能写,也能懂。
1. synchronized 的基本概念
synchronized 是 Java 内置的同步机制,用于解决多线程环境下的 共享资源访问冲突问题,保证:
- 原子性(Atomicity) – 同一时间只有一个线程执行被保护的代码块。
- 可见性(Visibility) – 一个线程修改的数据,对其他线程是可见的。
- 有序性(Ordering) – JVM 和 CPU 不会随意重排序 synchronized 内的代码。
2. 使用方式
2.1 修饰实例方法
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
}
- 锁对象:当前实例对象 (
this) - 效果:同一个对象的
increment方法在同一时间只能被一个线程执行
2.2 修饰静态方法
class Counter {
private static int count = 0;
public static synchronized void incrementStatic() {
count++;
}
}
- 锁对象:类对象 (
Counter.class) - 效果:同一个类的所有线程对静态方法访问是互斥的
2.3 修饰代码块
class Counter {
private int count = 0;
private final Object lock = new Object();
public void increment() {
synchronized (lock) {
count++;
}
}
}
- 锁对象:指定对象 (
lock) - 优势:锁的粒度更小,减少竞争,提高性能
3. 锁的本质
在 JVM 层面,synchronized 是依靠 对象头的 Monitor 锁(monitor) 实现的:
- 每个对象都有一个 对象头(Object Header),里面存放了 锁信息、哈希码、GC 分代年龄等元数据。
- 当线程进入
synchronized代码块:- JVM 会尝试 获取对象的 Monitor 锁
- 成功后线程执行代码,失败则阻塞或等待
- 退出代码块时释放锁
4. 锁的优化机制
Java 6 引入了多种优化机制来提高 synchronized 性能:
| 锁类型 | 特点 |
|---|---|
| 偏向锁(Biased Locking) | 单线程多次进入,不需要加锁释放,减少 CAS 操作 |
| 轻量级锁(Lightweight Lock) | 多线程竞争轻量,避免阻塞,使用自旋锁 |
| 重量级锁(Heavyweight Lock) | 多线程竞争严重,线程挂起等待,性能最慢 |
- JVM 会根据竞争情况自动升级锁的状态
- 偏向锁和轻量级锁是 自适应优化,一般无需手动干预
5. 注意事项
- 锁对象要唯一
synchronized(this)或synchronized(lock)- 不要用
new Object()每次生成新对象作为锁
- 避免死锁
- 保持锁获取顺序一致
- 尽量减少嵌套锁
- 性能影响
- 锁粒度太大 → 并发度低
- 使用锁对象代替方法锁可以优化
- 与
volatile的区别volatile只能保证 可见性synchronized保证 原子性 + 可见性
6. 总结
synchronized是 Java 最简单且安全的内置锁- 用法:实例方法、静态方法、代码块
- 原理:基于 对象 Monitor 锁 + JVM 优化机制
- 优化:偏向锁 → 轻量级锁 → 重量级锁
- 注意锁对象唯一、避免死锁、粒度优化
💡 小技巧:
- 如果只是修改单个字段,可以考虑
AtomicInteger等原子类,性能比synchronized更高。 - 对于高并发读场景,使用
ReadWriteLock可以提高效率。
我可以帮你画一张 synchronized 锁状态变化图,从偏向锁 → 轻量级锁 → 重量级锁的生命周期,直观理解 JVM 内部原理。
你希望我画吗?