05.原子性解决方案-互斥锁
总结
- 锁与受保护资源必须对应,不能用自己的锁保护别人的资源
- 细粒度锁提升并发性能,但要注意死锁风险
- 一次性申请所有资源可破坏"占用且等待"条件,但空转耗 CPU,用等待-通知机制优化
1. 锁的核心原则
- 锁与受保护资源必须对应,不能用自己的锁保护别人的资源
- 受保护资源与锁的关系是 N:1(多个资源可以用同一把锁)
- 细粒度锁:不同资源用不同的锁,提升并发性能
2. 常见错误示例
class Account {
private int balance;
// ❌ 错误:this 锁保护不到 target.balance
synchronized void transfer(Account target, int amt) {
if (this.balance > amt) {
this.balance -= amt;
target.balance += amt; // target 不在锁的保护范围内
}
}
}
解决方案:锁 Account.class(太重,所有转账串行)或用细粒度双重锁(注意死锁,见 死锁问题与解决方案)。
3. 一次性申请资源(破坏占用且等待)^54abc4
class Allocator {
private List<Object> als = new ArrayList<>();
synchronized boolean apply(Object from, Object to) {
if (als.contains(from) || als.contains(to)) return false;
als.add(from); als.add(to);
return true;
}
synchronized void free(Object from, Object to) {
als.remove(from); als.remove(to);
}
}
void transfer(Account target, int amt) {
while (!actr.apply(this, target)); // 空转耗 CPU,用 [[06.等待-通知机制]] 优化
try {
synchronized (this) {
synchronized (target) {
if (this.balance > amt) {
this.balance -= amt;
target.balance += amt;
}
}
}
} finally {
actr.free(this, target);
}
}
Lock 系列的底层实现见 AQS抽象队列同步器原理,Lock 相比 synchronized 的优势(可中断、超时、公平锁)见 09.并发工具类#Lock 与 Condition。