static synchronized方法锁的是该类的class对象,即类锁;它不依赖实例,所有线程调用该静态方法时竞争同一把锁,且与实例锁互不干扰。
java中synchronized修饰静态方法时,自动锁定的是该类的class对象——也就是所谓“类锁”。它不依赖任何实例,而是作用于整个类的生命周期,所有线程调用该静态同步方法时,都必须竞争同一把锁。
静态方法加synchronized = 隐式锁住Class对象
当你写:
public static synchronized void init() { ... }
等价于:
public static void init() {
synchronized (YourClass.class) {
// 方法体
}
}
JVM会自动以YourClass.class为锁对象。无需显式写出,但语义完全一致。
- 无论创建多少个该类的实例,所有线程调用这个静态方法时,都只争抢一把锁
- 即使没有实例存在(比如从未new过对象),静态方法仍可被调用并受锁保护
- 多个静态同步方法之间互斥:一个线程在执行
init(),另一个线程无法同时进入reset()
显式使用YourClass.class加锁更灵活
直接在代码块中锁XXX.class,可以精准控制临界区范围,避免锁整个方法带来的性能损耗。
- 适合做双重检查(如单例初始化、配置加载)
- 可在同一个静态方法里,只对真正需要同步的部分加锁
- 语义更清晰,明确表达“此处是类级别资源操作”
例如:
private static volatile DataSource ds;
public static DataSource getDataSource() {
if (ds == null) {
synchronized (DatabaseUtil.class) {
if (ds == null) {
ds = createPooledDataSource();
}
}
}
return ds;
}
类锁和实例锁完全独立,不会互相阻塞
这是初学者最容易混淆的点:
- 实例方法锁的是
this(某个具体对象) - 静态方法锁的是
MyClass.class(唯一的类元数据对象) - 一个线程在执行
obj.doWork()(实例同步方法),另一个线程完全可以同时执行MyClass.doInit()(静态同步方法)
二者锁的是不同对象,JVM视为两把毫不相干的锁。
什么时候必须用类锁
核心判断标准:操作的资源是否被该类**所有实例共享**,且与具体对象无关。
- 静态变量的读写(如计数器、缓存Map、开关标志)
- 单例模式的延迟初始化
- 全局配置加载、日志器初始化、连接池启动
- 类级别的状态变更(如启用/禁用某种功能模块)
如果资源属于实例独有(如每个对象维护自己的id、name),就该用实例锁或自定义对象锁,而不是类锁。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











