不能用integer等包装类作为synchronized锁对象,因其不可变性导致每次自增都创建新对象,且jvm缓存-128~127的integer值造成锁对象意外共享,使同步失效;应使用private final object lock = new object()或concurrenthashmap管理唯一锁。

不能用Integer等包装类作为synchronized锁对象,根本原因在于它们既不可变,又受JVM缓存机制影响,导致锁对象在运行中“悄悄换人”或“意外共享”,同步逻辑彻底失效。
缓存复用:看似不同的锁,其实是同一把
Java对-128到127之间的Integer值做了缓存(IntegerCache)。比如:
- Integer a = 100; 和 Integer b = 100; —— 两者== true,指向同一个对象
- 多个业务模块若各自声明
private Integer lock = 100;,实际共用一把锁 - 本该互不干扰的线程,因锁对象被复用而被迫串行执行,严重拖慢并发性能
不可变性:每次自增都在更换锁主
Integer是不可变类,count++不是修改原对象,而是执行:
- 读取当前值(如
Integer(5)) - 计算新值(6)
- 调用
Integer.valueOf(6)创建新对象,并让count变量指向它 - 下一次进入synchronized时,锁的是全新的
Integer(6),和上一轮完全无关
结果就是:多个线程轮流锁不同对象,临界区形同虚设,数据竞争必然发生。
final修饰也救不了——只防替换,不防共享
加final Integer lock = 100;能防止变量被重新赋值,但无法绕过缓存:
- 它仍落在-128~127范围内,依然会与其它
final Integer x = 100;共享同一缓存实例 - 锁的“唯一性”无法保障,隔离性依旧崩溃
- 看似写死了,实则埋得更深、更难排查
安全替代方案:简单、确定、无副作用
真正可靠的锁对象必须满足两个条件:唯一性 + 不可变引用。推荐做法:
-
用
private final Object lock = new Object();——每次new都生成独立实例,无缓存、无复用 - 若需按ID区分锁(如用户粒度),用
ConcurrentHashMap<integer object></integer>做锁容器,配合computeIfAbsent确保每个ID对应唯一锁对象 - 避免使用String、Long、Boolean等所有基础包装类,它们同样存在缓存或不可变问题
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











