synchronized实现线程安全计数器需分三种场景:1. 实例方法同步,以this为锁;2. 同步代码块,用private final对象显式加锁;3. 静态变量须用类锁(Counter.class),禁混用实例锁与类锁。

用 synchronized 实现线程安全的计数器,核心是确保对共享变量(如 count)的读-改-写操作(++ 或 --)具有原子性,避免多个线程同时执行导致结果错乱。
同步方法:最直接的方式
把增减操作封装在 synchronized 修饰的实例方法中,JVM 会自动以当前对象(this)为锁,保证同一时刻只有一个线程能进入该方法:
public class Counter {
private int count = 0;
public synchronized void increment() {
count++; // 等价于 count = count + 1;整个操作被锁保护
}
public synchronized void decrement() {
count--;
}
public synchronized int getValue() {
return count;
}
}
- 适合单个共享实例被多线程共用的场景(如全局计数器)
- 注意:如果多个线程操作的是不同
Counter实例,那每个实例的锁互不干扰,无法保证跨实例的线程安全
同步代码块:更灵活、粒度更细
当类中还有其他非临界逻辑,或需要复用同一把锁控制多个变量时,推荐用 synchronized(锁对象) 块,显式指定锁:
public class Counter {
private int count = 0;
private final Object lock = new Object(); // 显式锁对象,避免锁 this 带来意外耦合
public void increment() {
synchronized (lock) {
count++;
}
}
public void decrement() {
synchronized (lock) {
count--;
}
}
public int getValue() {
synchronized (lock) {
return count;
}
}
}
- 锁对象建议用
private final的专用对象,不暴露、不变更,防止外部误同步造成死锁或失效 - 比同步方法更可控:可只锁真正需要保护的代码段,减少阻塞范围
静态计数器:需要用类锁
如果计数器是 static 的(属于类而非实例),就必须用类级别的锁(Counter.class 或 Class.forName("...").getClass()),否则实例锁无效:
立即学习“Java免费学习笔记(深入)”;
public class Counter {
private static int count = 0;
public static synchronized void increment() {
count++; // 锁的是 Counter.class,所有线程竞争同一把锁
}
// 或等价写法:
public static void decrement() {
synchronized (Counter.class) {
count--;
}
}
}
- 静态同步方法隐式使用当前类的
Class对象作为锁 - 不能混用实例锁和类锁来保护同一个静态变量,否则线程安全不成立
注意事项与常见误区
- 不要用 String、Integer 等常量池对象做锁:它们可能被其他代码复用,导致意外交互或死锁
-
避免在同步块内调用外部可重入方法:比如在
synchronized中调用另一个类的同步方法,容易引发嵌套锁或死锁 -
synchronized 不能保证可见性以外的全部语义:它既保证原子性也保证可见性(JMM 中的 happens-before),但若需更高性能或复杂操作(如 CAS、条件等待),应考虑
java.util.concurrent.atomic.AtomicInteger或ReentrantLock


















