面试题答案
一键面试常见锁机制在高并发场景下的性能问题
- 互斥锁
- 竞争激烈:在高并发环境中,多个线程频繁竞争互斥锁,导致大量线程阻塞,线程上下文切换开销增大,降低系统整体性能。
- 死锁风险:如果多个线程获取锁的顺序不当,容易形成死锁,使系统部分或全部功能无法继续运行。
- 读写锁
- 写锁饥饿:读操作通常比写操作频繁,若大量读线程持续获取读锁,写线程可能长时间无法获取写锁,造成写操作饥饿。
- 读锁同步开销:虽然读锁允许多个线程同时读取,但为了保证数据一致性,在获取和释放读锁时仍需进行同步操作,高并发时这也会带来一定性能开销。
创新性锁优化方案 - 乐观锁
- 设计思路 乐观锁假设在大多数情况下,并发操作不会发生冲突。它不使用传统的加锁机制,而是在数据更新时检查数据在读取后是否被其他线程修改。如果未被修改,则更新成功;否则,重试操作。
- 实现要点
- 版本号机制:为数据添加版本号字段。每次读取数据时,同时获取版本号。在更新数据时,将当前版本号与数据中的版本号进行比较,如果相同,则更新数据并将版本号加1;如果不同,说明数据已被其他线程修改,放弃本次更新并重新读取数据再尝试更新。
- CAS(Compare - And - Swap)操作:这是实现乐观锁的核心操作。CPU 提供的 CAS 指令能够在硬件层面实现比较并交换操作,保证操作的原子性。例如,在 Java 中,
java.util.concurrent.atomic
包下的原子类就是基于 CAS 实现的。
- 可能带来的风险
- ABA 问题:如果一个数据从 A 变成 B,再变回 A,使用版本号机制时,乐观锁可能认为数据未被修改,但实际上中间经历了变化。可以通过添加额外的时间戳或使用更复杂的标记机制来解决。
- 重试开销:如果并发冲突频繁,重试操作会增加系统开销,降低性能。因此,乐观锁适用于读多写少,并发冲突较少的场景。