
本文深入剖析java多线程中synchronized关键字未生效的根本原因:实例方法同步无法保护静态共享变量,导致竞态条件;并给出基于类锁、对象锁与最佳实践的完整解决方案。
本文深入剖析java多线程中synchronized关键字未生效的根本原因:实例方法同步无法保护静态共享变量,导致竞态条件;并给出基于类锁、对象锁与最佳实践的完整解决方案。
在Java并发编程中,synchronized 是最基础也最容易被误用的同步机制。许多开发者遇到类似问题:明明加了 synchronized,却仍出现计数器结果不一致(如期望20,000,实际仅15,327)、数据覆盖或脏读等现象。其核心症结往往不在语法错误,而在于锁对象与共享状态的粒度不匹配。
以原始代码为例,关键问题在于:
- cnt 是 static int,属于类级别共享状态,被所有 Runner 实例共同访问;
- 但 func() 是实例方法且声明为 synchronized,等价于:
public void func() { synchronized (this) { // ← 锁的是当前实例(r1 或 r2),非同一对象! cnt++; } } - 由于创建了两个独立的 Runner 实例(r1 和 r2),两个线程分别在 r1 和 r2 上执行 func(),实际获取的是两把互不相关的锁,对 cnt 的读-改-写操作完全未受保护,从而引发典型的竞态条件(Race Condition)。
✅ 正确解法一:使用静态同步方法(推荐用于静态变量)
将 func() 改为 static synchronized,使锁升级为类锁(即 Runner.class 对象):
class Runner extends Thread {
private static int cnt = 0;
// ✅ 正确:静态同步方法 → 锁住 Runner.class(唯一类对象)
public static synchronized void func() {
cnt++;
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
func();
}
}
public void print() {
System.out.println("Final cnt = " + cnt);
}
}? 原理说明:static synchronized 等价于 synchronized (Runner.class)。每个类加载器下,Runner.class 是全局唯一的 Class 对象,因此所有线程竞争同一把锁,确保对 cnt 的修改具有原子性与可见性。
立即学习“Java免费学习笔记(深入)”;
Alibabacloud Sdk Client Initialization For Java下载在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
✅ 正确解法二:显式使用类锁(更灵活、语义清晰)
若需在非静态方法中操作静态变量,可手动同步 Runner.class:
public void run() {
for (int i = 0; i < 10000; i++) {
synchronized (Runner.class) { // 显式指定类锁
cnt++;
}
}
}⚠️ 注意事项与进阶提醒
- 不要混淆“锁对象”与“被保护数据”:同步的目的是让所有访问同一共享变量的代码路径都通过同一把锁串行化。锁对象必须一致,否则形同虚设。
- 避免过度同步:若 cnt 改为实例变量(private int cnt),则原 synchronized func() 就是正确的——此时锁与数据粒度匹配(每个实例独享自己的 cnt)。但本例中因 cnt 是静态的,必须提升锁级别。
-
现代替代方案建议:对于简单计数场景,优先考虑 AtomicInteger:
private static AtomicInteger cnt = new AtomicInteger(0); public void run() { for (int i = 0; i < 10000; i++) { cnt.incrementAndGet(); // 无锁、高效、线程安全 } }它基于CAS(Compare-and-Swap)实现,性能通常优于 synchronized,且无需手动管理锁。
✅ 验证与总结
修正后运行程序,输出稳定为 Final cnt = 20000,证明同步生效。总结关键原则:
- ✅ 锁对象一致性:保护同一共享状态的所有同步块/方法,必须使用同一个锁对象;
- ✅ 锁粒度匹配:静态变量 → 类锁(ClassName.class 或 static synchronized);实例变量 → 实例锁(this 或 synchronized 实例方法);
- ✅ 优先选择高级API:java.util.concurrent.atomic 包提供更简洁、高性能的无锁原子操作,应作为首选。
掌握锁与共享状态的映射关系,是写出可靠并发代码的第一道门槛。理解 synchronized 的本质不是“加个关键字”,而是精确控制哪段代码在哪个锁的保护下执行——这才是Java并发编程的底层逻辑。


















