乐观锁和悲观锁

2019-05-27  本文已影响0人  沐兮_d64c

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(比较与交换),需要与volatile配合保证线程安全。
AtomicInteger:利用Unsafe的cas和volatile关键字保证线程安全。
当且仅当内存值=expect值时,才将内存值更新为update。

image.png
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);
  }

}
上一篇 下一篇

猜你喜欢

热点阅读