05.原子性解决方案-互斥锁

总结
  • 锁与受保护资源必须对应,不能用自己的锁保护别人的资源
  • 细粒度锁提升并发性能,但要注意死锁风险
  • 一次性申请所有资源可破坏"占用且等待"条件,但空转耗 CPU,用等待-通知机制优化

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