1,乐观锁与悲观锁概念
1)概念:
两种不同的思想,为了解决并发场景下的数据竞争。不局限于语言和数据库。
2)乐观锁:操作数据时,认为别人不会修改,所以不会加锁,只在更新数据时,先查询判断下别人是否修改了数据,是则放弃,否则继续操作。如版本号机制和CAS.
3)悲观锁:操作时直接加锁数据,操作完成才释放,期间其他人不能修改数据。如synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。
4)应用场景
悲观锁:用于写入、更新比较多的场景。
乐观锁:用于读取比较多,写入、更新比较少的场景
2,版本号机制实现乐观锁
1)版本号机制:
为数据增加一个version字段,更新数据时,先读出version,提交时,判断真实的version是否和刚读出的version一致。
2)数据更新流程。
query old verifyCode;
generate new VerifyCode;
update tb_account set amount = ?, verify_code = ? where userId = ? and verify_code = ?;
3)数据更新流程,通常和retry重试(如:guava-retrying
)绑定。retry单元中,先query,然后执行update,失败则重试。
3,CAS实现乐观锁
1)compare and swap(
image.png比较与交换
),需要与volatile配合保证线程安全。
AtomicInteger:利用Unsafe的cas和volatile关键字保证线程安全。
当且仅当内存值=expect值时,才将内存值更新为update。
2)ABA问题
线程1读取时记录为A,线程2多次修改后记录仍为A,线程1进行CAS操作。
使用AtomicStampedReference解决ABA问题。
image.png
image.png
3)CAS自旋锁
public class MySpinLock {
//可以原子更新的对象引用。
private AtomicReference<Thread> cas = new AtomicReference<Thread>();
//尝试加锁
public void tryLock() {
Thread current = Thread.currentThread();
// 利用CAS, 获取失败则进行自旋
while (!cas.compareAndSet(null, current)) {
// DO nothing
}
}
//尝试解锁
public void unlock() {
Thread current = Thread.currentThread();
cas.compareAndSet(current, null);
}
}
网友评论