必须用类锁而非对象锁,因为类锁(如sequencegenerator.class)确保所有实例共享同一把锁,实现跨实例的串行化访问;若用实例锁,各对象独立加锁,无法保证全局序号唯一。

Java中用 synchronized 实现全局序号生成器的原子性,关键在于**使用类锁(Class-level lock)确保所有实例共享同一把锁**,从而避免多线程环境下序号重复或跳变。
为什么必须用类锁而不是对象锁
如果用普通实例方法加 synchronized,每个生成器对象持有一把独立的对象锁,多个线程操作不同实例时互不干扰,序号就无法全局唯一。而类锁作用于 ClassName.class,所有线程无论通过哪个实例调用,都竞争同一把锁,真正实现跨实例的串行化访问。
正确写法:静态同步方法 or 同步代码块 + 类字面量
推荐两种等效方式,语义清晰且不易出错:
-
静态同步方法:直接在静态方法上加
synchronized,JVM 自动以当前类的Class对象为锁 -
同步代码块 +
XXX.class:更灵活,可精确控制临界区范围,避免锁粒度过大
示例代码:
public class SequenceGenerator {
private static long counter = 0;
// 方式1:静态同步方法(简洁)
public static synchronized long nextId() {
return ++counter;
}
// 方式2:同步代码块(推荐,锁范围更小)
public static long nextIdV2() {
synchronized (SequenceGenerator.class) {
return ++counter;
}
}
}
注意点:避免锁对象被替换或误用
类锁依赖于 Class 对象的唯一性和稳定性。需注意:
- 不要在同步块中使用可能为
null的类引用(如obj.getClass()在子类场景下可能不一致) - 避免用字符串字面量或自定义常量作为锁对象(如
synchronized("lock")),容易因字符串驻留导致意外锁竞争或死锁 - 若类被多个类加载器加载(如OSGi、Web容器),不同加载器下的
Class对象不相等,此时类锁失效——这种场景需改用分布式锁或外部协调服务
性能与扩展性提醒
类锁是强一致性方案,简单可靠,但高并发下会成为瓶颈。如果吞吐量要求极高,可考虑:
- 分段计数(如按线程ID哈希分配本地缓存段,定期合并)
- 使用
AtomicLong.incrementAndGet()替代手动同步(底层基于CAS,无锁且高效) - 引入时间戳+机器ID+序列号的雪花算法(Snowflake),适合分布式环境
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











