使用synchronized同步代码块可精准控制锁粒度,提升并发性能;应指定专用锁对象,避免String等易复用对象,并仅将真正需要互斥的临界区代码置于块内。

在 Java 中,用 synchronized 锁定指定代码块,核心是使用同步代码块语法:synchronized(锁对象) { ... },而不是直接修饰整个方法。这样做能避免无谓的线程阻塞,把锁的范围精确控制在真正需要互斥的那几行代码上,显著提升并发性能。
明确指定锁对象,避免隐式锁带来的粒度问题
方法级 synchronized(如 public synchronized void doWork())默认用 this(实例锁)或类对象(静态方法锁),粒度太粗。改用同步代码块后,你可以自主选择锁对象:
- 多个操作共享同一资源时,用该资源本身作为锁对象(例如:一个
ArrayList实例) - 若需隔离不同业务逻辑,可创建专用的私有锁对象(推荐):
private final Object lock1 = new Object(); - 避免使用
String、常量池对象或Integer等可能被复用的对象作锁,以防意外的锁竞争
只包裹真正需要同步的临界区代码
把非共享、非状态变更的操作(如日志打印、本地变量计算、IO 预处理)移出同步块,仅保留读写共享变量、修改对象状态、调用非线程安全方法等关键步骤:
// ✅ 好的做法:锁粒度精准
public void transfer(Account from, Account to, BigDecimal amount) {
// 非共享操作:参数校验、金额计算 —— 不加锁
if (amount.compareTo(BigDecimal.ZERO) <= 0) throw new IllegalArgumentException();
// 仅对账户余额更新加锁(假设用 account 本身作锁)
synchronized (from) {
if (from.getBalance().compareTo(amount) < 0) {
throw new InsufficientBalanceException();
}
from.debit(amount);
}
synchronized (to) {
to.credit(amount);
}
}
注意锁对象的可见性与生命周期一致性
锁对象必须在所有需要同步的线程间“看到的是同一个实例”:
立即学习“Java免费学习笔记(深入)”;
- 实例锁对象应声明为
final,确保构造后不可变且线程可见 - 不要在同步块内 new 新锁对象,否则每次锁的都不是同一个对象,失去同步意义
- 若涉及多个共享资源(如两个账户转账),需按固定顺序加锁(如按账户 ID 升序),防止死锁
对比:同步方法 vs 同步代码块的实际效果
假设一个服务类中有三个方法:A(读配置)、B(更新缓存)、C(发通知)。只有 B 操作共享缓存 Map:
- 若整个 B 方法用
synchronized修饰,则 A 和 C 调用时也会因锁住 this 而排队等待——不必要 - 改用
synchronized(cacheMap) { cacheMap.put(key, value); },A 和 C 完全不受影响,B 内部也只锁住真正修改的那行


















