选对锁对象是synchronized生效的前提,需满足唯一性、可见性、稳定性;推荐private final Object专用锁,避免用this、字符串字面量、包装类等易引发问题的对象。

选对锁对象,是 synchronized 代码块真正起作用的前提。不是随便找个对象就能锁住共享资源,关键在于“唯一性、可见性、稳定性”——多个线程必须能访问到**同一个、不变的、专用于同步的对象**。
推荐用 private final Object 作专用锁
这是最规范、最安全的做法:
- 声明为 private:防止外部误调用或意外加锁
- 声明为 final:确保引用不可变,锁对象生命周期稳定
- 显式新建 new Object():避免复用可能被其他逻辑共用的对象(比如 this、字符串、返回值等)
示例:
private final Object balanceLock = new Object();synchronized (balanceLock) { balance += amount; }
按业务场景选更合适的锁对象
有时专用 Object 不够贴合语义,可结合数据边界选择:
立即学习“Java免费学习笔记(深入)”;
- 操作某个用户账户时,直接用 user 对象本身(前提是 user 实例全局唯一且不被替换)
- 扣减某商品库存时,用 itemId 对应的锁(例如缓存在 ConcurrentMap
中) - 保护静态配置或全局计数器,可用 ClassName.class(等效类锁)
明确要避开的锁对象
这些看似方便,实则容易引发隐性问题:
- this:除非所有调用都来自同一实例,否则不同实例之间无法互斥
- String 字面量(如 "lock"):受字符串常量池影响,可能跨类意外共用同一把锁
- 基本类型包装类(如 Integer.valueOf(100)):有缓存机制,值相同时引用可能相同
- 方法内 new 出的对象:每次执行都新建,锁对象不一致,等于没锁
- null 或可能被重赋值的变量:运行时报 NullPointerException 或锁失效
验证锁对象是否真正“同一把”
最简单的方法是在关键位置打印锁对象的 hashCode 或 toString:
System.out.println("Lock obj: " + lock);多个线程进入同步块时输出的地址应完全一致。若每次都不一样,说明锁对象没共享好,需回查初始化和引用传递逻辑。


















