synchronized 避免数据不一致需用对锁对象、控制同步范围、保障业务原子性:锁对象应私有 final,如 private final Object lock = new Object();仅同步必要代码;复合操作(如查余额→扣款)必须整体加锁;synchronized 天然保证可见性与有序性。

synchronized 能避免数据不一致,关键不是“用了就行”,而是用对对象、锁住范围、守住边界。
锁对象要私有且不可变
别用 this、String 或 Integer 做锁——前者可能被外部调用干扰,后者可能因自动装箱/字符串常量池导致实际锁对象不一致。推荐声明一个私有的 final Object:
- private final Object lock = new Object();
- 所有需要互斥的代码块都统一用它:synchronized(lock) { ... }
- 这样能确保锁对象唯一、稳定、不被外部篡改
只锁真正需要同步的代码段
粒度越小,并发效率越高,出错概率越低。不要把日志、网络请求、数据库查询等无关操作包进 synchronized 块里:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- 错误写法:synchronized(lock) { log.info("开始"); count++; db.save(count); }
- 正确写法:log.info("开始"); synchronized(lock) { count++; } db.save(count);
- count++ 是非原子操作(读-改-写三步),只有这一步必须受保护
注意复合操作仍需整体加锁
AtomicInteger 可以安全做 count.incrementAndGet(),但像“查余额→够就扣”这种两步判断+更新,CAS 无法一次完成,必须用 synchronized 包裹整个逻辑:
- 例如:synchronized(lock) { if (balance >= amount) balance -= amount; }
- 否则可能出现:线程A查到余额足够,正准备扣款时被挂起;线程B也查到同样余额并扣款成功;线程A恢复后继续扣,造成超支
- 这种业务级原子性,只能靠显式锁保障
别忽略内存可见性与重排序影响
synchronized 天然提供可见性和有序性:释放锁前的写操作对下一个获取该锁的线程一定可见,且不会被重排序到锁外:
- 这意味着,只要共享状态的读写都在同一把锁下,就不用额外加 volatile
- 但如果部分字段在锁外读写(比如某个标志位单独用 volatile 修饰),要小心它们和锁内变量的执行顺序是否符合预期
- 简单原则:同一批关联状态,尽量统一用同一把锁保护
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










