Redis没有原子性,所以当前事务在操作时,其他事务可以修改,造成错误
Redis还没有隔离性

悲观锁和乐观锁

悲观锁

  • 很悲观,认为什么时候都会出问题,无论做什么都会上锁

乐观锁

  • 很乐观,每次拿数据的时候都认为别人不会修改,所以都不上锁。但是会对数据进行监听,在更新的时候会判断在此期间有没有别人对此数据进行修改。如果有修改,则当前事务执行失败。
  • 可以使用版本号(Version)机制和CAS算法实现
  • 乐观锁适应于多读的应用场景,这样可以提高吞吐量

Redis的监控 Watch

  • Redis实现乐观锁是基于版本号(Version)机制的
# 模拟转账业务
set money 100
set out 0
watch money #监控 money
muti
decrby money 20
incrby out 20
# 如果在事务提交之前,其他事务对money进行了修改,则 money的 版本号version 增加
# set money 500;
# 此时事务时,会比较version  版本不一样,则事务提交失败 不会更新 
exec
# 此时要继续进行转帐 ,则 需要解锁 获取下一个乐观锁
unwatch money #先解锁
watch money  #获取新的乐观锁
muti
decrby money 20
incrby out 20
exec # exec的时候 版本号没有修改 则正常执行

CAS算法实现乐观锁

  • compare and swap(比较与交换)
  • 不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步
    CAS算法涉及到三个操作数
  • 需要读写的内存值 V
  • 进行比较的值 A
  • 拟写入的新值 B
    当且仅当 V 的值等于 A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试。

CAS乐观锁的缺点

ABA问题

  • cas算法会造成ABA问题
  • 假如其他线程在被上锁期间,对其值进行了修改但最后又改回了原值,CAS算法会误认为他从来没有被修改过

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

  • 只对单个共享变量有效,当操作涉及多个共享操作时,将会无效,但是从 JDK 1.5开始,提供了AtomicReference类来保证引用对象之间的原子性,可以将多个变量封装成一个变量来进行操作

循环时间长开销大

  • cas采用自旋锁,如果一直操作失败,则会给CPU造成非常大的执行开销