synchronized修饰静态方法是通过以该类的Class对象为锁来保障static变量线程安全,确保所有线程串行执行,等价于synchronized(MyClass.class)块。

用 synchronized 修饰静态方法,是保护类变量(即 static 变量)最直接、最常用的方式。它的核心不是“锁变量”,而是用当前类的 Class 对象作锁,确保所有线程对静态变量的读写操作串行执行。
为什么静态方法加 synchronized 能保护 static 变量
静态变量属于类,被所有实例共享,只有一个副本。要保证多线程安全,必须用一个所有线程都认可的、唯一的锁——这个锁就是 MyClass.class。JVM 在遇到 public static synchronized void method() 时,自动以该类的 Class 对象为监视器(monitor),等价于:
synchronized(MyClass.class) { /* 方法体 */ }- 无论创建多少个对象,甚至不创建任何实例,只要调用该静态同步方法,就竞争同一把锁
- 多个静态同步方法共用这把锁,彼此互斥
正确写法与典型示例
下面是一个安全递增全局计数器的标准写法:
public class Counter {
private static int totalCount = 0;
<pre class="brush:php;toolbar:false;">// ✅ 正确:静态同步方法,保护 static 变量
public static synchronized void increment() {
totalCount++;
}
// ✅ 等价写法:显式使用类锁的同步块
public static void incrementWithBlock() {
synchronized (Counter.class) {
totalCount++;
}
}
// ❌ 错误:实例同步方法无法保护 static 变量
public synchronized void badIncrement() {
totalCount++; // 多个线程调用不同实例的该方法,锁的是 this,不互斥
}}
立即学习“Java免费学习笔记(深入)”;
常见误区与避坑点
实际开发中容易踩的几个坑:
-
混淆锁对象和数据作用域:synchronized 静态方法只保证方法体串行执行,但如果你在其中操作的是
this.xxx(实例字段),那依然线程不安全——因为锁的是类,数据却分散在各实例上 -
误以为“synchronized + static 变量声明”就安全:仅声明
private static int x;没有任何同步效果;必须让所有读写路径都经过同一把类锁 -
忽略类加载器隔离:同一类名,若由不同
ClassLoader加载(如 Web 容器中的多个模块),会产生不同的MyClass.class实例,此时静态同步方法无法跨模块互斥。需改用全局唯一标识的显式锁(如ReentrantLock配合 JVM 级协调)
什么时候选静态方法?什么时候选同步代码块?
两者效果一致,选择取决于控制粒度需求:
- 用
static synchronized方法:逻辑简单、整个方法体都是临界区、可读性优先 - 用
synchronized(MyClass.class) { ... }块:方法内只有部分代码需同步;或需在一个块里原子操作多个 static 变量;或想明确表达“这里用类锁护住这几行” - 避免在静态同步方法里做耗时非临界操作(如 I/O、远程调用),否则会无谓阻塞其他线程


















