
本文深入剖析java中synchronized关键字因锁对象不一致导致线程安全失效的经典问题,阐明静态共享变量必须配合类级别同步锁(如static synchronized或synchronized(classname.class)),并通过修正代码和关键对比示例给出可落地的并发修复方案。
本文深入剖析java中synchronized关键字因锁对象不一致导致线程安全失效的经典问题,阐明静态共享变量必须配合类级别同步锁(如static synchronized或synchronized(classname.class)),并通过修正代码和关键对比示例给出可落地的并发修复方案。
在Java多线程编程中,synchronized 是保障临界区互斥访问最基础也最易误用的机制。一个常见误区是:对静态共享变量使用实例方法级synchronized,却期望实现跨实例的线程安全——这正是本例中 cnt 最终值远小于预期 20,000 的根本原因。
问题核心在于锁对象的粒度与共享状态范围不匹配。原代码中:
- cnt 是 static 变量,被所有 Runner 实例共享;
- func() 是实例方法且声明为 synchronized,等价于:
public void func() { synchronized (this) { // ← 锁的是当前实例(r1 或 r2),彼此无关! cnt++; } } - 由于创建了两个独立的 Runner 对象(r1 和 r2),两个线程分别在 r1 和 r2 上执行 func(),实际获取的是两把不同的锁,对 cnt 的读-改-写操作完全未受保护,引发典型的竞态条件(Race Condition)。
✅ 正确解法:让同步作用于同一把锁,且该锁必须与静态变量 cnt 的生命周期和可见性范围一致。推荐两种等效方案:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
方案一:将方法改为 static synchronized(推荐)
class Runner extends Thread {
private static int cnt = 0;
// ✅ 静态同步方法:锁对象为 Runner.class(全局唯一)
public static synchronized void func() {
cnt++;
}
public void run() {
for (int i = 0; i <h3>方案二:显式使用类对象锁(更灵活,便于扩展)</h3><pre class="brush:php;toolbar:false;">public void func() {
synchronized (Runner.class) { // ✅ 明确锁定 Runner 类对象
cnt++;
}
}? 关键原理:Runner.class 是 JVM 在类加载时创建的 Class
单例对象,无论多少个 Runner 实例,Runner.class 始终是同一个对象,从而确保所有线程竞争同一把锁。
完整修正版主程序(含健壮等待逻辑)
public class Synchronization {
public static void main(String[] args) throws InterruptedException {
Runner r1 = new Runner();
Runner r2 = new Runner();
r1.start();
r2.start();
// ✅ 使用单次 try-catch 合并等待,更简洁
r1.join();
r2.join();
r1.print(); // 输出:Final cnt = 20000
}
}⚠️ 注意事项与最佳实践
- 不要混用锁粒度:静态变量 → 必须用类锁(static synchronized 或 synchronized(ClassName.class));实例变量 → 可用实例锁(synchronized(this) 或 synchronized(instanceRef))。
- 避免锁泄漏:若需保护多个静态字段,应统一使用同一把类锁,而非分散使用不同锁对象。
- 性能考量:类锁是全局锁,高并发下可能成为瓶颈;若业务允许,优先考虑无锁方案(如 AtomicInteger)或更细粒度锁(如 ReentrantLock 配合条件队列)。
-
替代方案验证(推荐生产环境使用):
private static AtomicInteger cnt = new AtomicInteger(0); // 线程安全,无需 synchronized public void func() { cnt.incrementAndGet(); // 原子操作,性能更高 }
综上,synchronized 的有效性不取决于关键字本身,而取决于所有访问共享状态的线程是否在逻辑上“看到同一把锁”。理解锁对象(this vs Class>)与共享状态(实例变量 vs 静态变量)的映射关系,是写出正确并发代码的第一道门槛。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










