静态同步的锁对象是类的class实例,该对象在类加载链接阶段后即创建且唯一;多线程调用静态同步方法时竞争同一class锁,jvm确保类初始化线程安全,但需避免在中交叉调用其他类静态同步方法以防死锁。

Java 中 synchronized 处理类加载过程中的静态同步,核心在于:静态同步方法或静态同步代码块所用的锁对象是该类的 Class 对象,而这个对象在类加载的「初始化阶段」(<clinit></clinit> 方法执行时)就已存在且唯一。因此,静态同步天然与类加载过程协同,但需注意锁的获取时机和竞争范围。
静态同步的锁对象是 Class 实例
当使用 public static synchronized void method() 或 synchronized(MyClass.class) { ... } 时,JVM 实际上是对当前类的 Class 对象加锁。这个 Class 对象由类加载器在「链接阶段的准备之后、初始化阶段开始前」创建,在整个 JVM 生命周期中唯一且不可变。
- 多个线程同时首次调用同一个类的静态同步方法,会竞争同一把锁(即
MyClass.class) - 即使类尚未完成初始化(比如
<clinit></clinit>正在执行),只要Class对象已存在,锁机制就可正常工作 - 类初始化本身也是线程安全的:JVM 保证一个类的
<clinit></clinit>最多只被一个线程执行,其余线程阻塞等待——这背后正是基于Class对象的隐式锁
静态同步方法与类初始化的顺序关系
静态同步方法不会触发类初始化,除非它首次被主动调用且类尚未初始化。此时 JVM 会先确保类完成初始化(执行 <clinit></clinit>),再执行该方法。这个过程是原子的:
- 线程 A 调用
MyClass.staticSyncMethod(),发现类未初始化 → JVM 获取MyClass.class锁,执行<clinit></clinit>,完成后执行方法体 - 线程 B 同时调用同一方法 → 在
Class对象上等待,直到 A 完成初始化并释放锁,B 才继续(无需重复初始化) - 也就是说,静态同步方法的执行,天然串行化在类初始化完成之后;你不需要额外同步来保护静态变量的首次赋值
避免在 中调用其他类的静态同步方法
类初始化阶段(<clinit></clinit>)应尽量轻量。若在其中调用另一个尚未初始化类的静态同步方法,可能引发死锁:
- 类 A 的
<clinit></clinit>中调用类 B 的静态同步方法 → 尝试获取B.class锁 - 类 B 的
<clinit></clinit>又调用了类 A 的静态同步方法 → 尝试获取A.class锁 - 两个线程分别持有对方需要的锁,形成循环等待
- 这种死锁不抛异常,程序挂起,排查困难
推荐写法:显式用 Class 对象做锁,更清晰可控
比起直接修饰静态方法,建议在关键静态逻辑中使用同步代码块,并明确写出锁对象:
public class ConfigLoader {
private static volatile Config instance;
public static Config getInstance() {
if (instance == null) {
synchronized (ConfigLoader.class) { // 明确锁目标
if (instance == null) {
instance = loadFromDB(); // 可能含耗时IO
}
}
}
return instance;
}
}
这样既复用类加载期已有的 Class 锁,又避免了静态方法带来的隐式范围(比如整个方法体都被锁住),也便于后续替换为更细粒度的锁策略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











