浅谈乐观锁与悲观锁

一. 定义

悲观锁:

总是假设最坏情况, 每次去拿数据的时候都认为别人会修改, 所以每次在拿数据的时候都会上锁, 这样别人拿数据的时候就会阻塞, 直到锁被释放. 传统数据库用到了很多这种锁机制, 比如行锁, 表锁, 读锁, 写锁等, 都是在做操作之前先上锁. Java 中 synchronized 和 ReentrantLock 等独占锁就是悲观锁思想的实现.

乐观锁:

总是假设做好的情况, 每次去拿数据的时候都认为别人不会修改, 所以不会上锁, 但是在更新的时候会判断一下在此期间别人有没有去更新这个数据, 可以使用版本号机制和 CAS 算法实现. 乐观锁适用于多读的应用类型, 这样可以提高吞吐量, 想数据库提供的类似于 write_condition 机制, 其实都是提供的乐观锁. 在 Java 中 java.util.concurrent.atomic 包下面的原子变量类就是使用了乐观锁的一种实现方式 CAS 实现的.

二. 两种锁的使用场景

从上面对两种锁的介绍, 我们知道两种锁各有各的优缺点, 不可认为一种好于另一种, 乐观锁适用于比较倾向于读的情况下, 这样能省去锁的开销, 加大系统的吞吐量. 但如果是多写的情况, 一般经常产生冲突, 这样反而会降低性能, 所以一般在多写的场景下使用悲观锁

三. 乐观锁常见的两种实现方式

乐观锁一般会使用版本号机制或 CAS 算法实现.

1. 版本号机制

一般是在数据表中加上一个数据版本号 version 字段, 表示数据被修改的次数, 当数据被修改时,  version 值就会加一. 当线程 A 要更新数据值时, 在读取数据的同时也会读取 version 值, 在提交更新时, 若刚才读取到的 version 值为当前数据库中的 version 值时才更新, 否则重试更新操作, 直到成功更新.

2. CAS 算法

compare and swap(比较与交换), 是一种有名的无锁算法. 在不使用锁的情况下实现多线程之间的变量同步, 所以也叫非阻塞同步

CAS 算法设计到的三个操作数

  • 需要读写内存值 V
  • 进行比较的值 A
  • 将写入内存的新值 B

当且仅当 V 的值等于 A 时, CAS 通过原子方式用新值 B 来更新 V 的值, 否则不会执行任何操作(比较和替换是一个原子操作). 一般情况下是一个自旋操作, 即不断地重试

四. 乐观锁的缺点

1.ABA问题

如果一个变量 V 初次读取的时候是 A 值, 并且在准备赋值的时候检查到它仍然是 A 值, 但在此期间 这个 A 值可能被修改为了 B 值, C 值等等, 如果又变回了 A 值.

2. 循环时间长开销大

自旋 CAS 如果长时间不成功, 会给 CPU 带来非常大的开销. 

3. 只能保证一个共享变量的原子操作

CAS 只对单个共享变量有效, 当操作涉及跨多个共享变量时 CAS 无效.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值