乐观锁:超卖和CAS的思考

乐观锁

众所周知,乐观锁是相对于悲观锁而言的,虽然叫锁但其实是无锁编程。
乐观锁比较常见的实现方式有:版本号机制和CAS

版本号机制

对于涉及到的数据会有一个额外的version字段,表示其版本。线程在修改数据前,会先读取到当前的版本号。提交的修改数据也会附带有这个版本号。如果这个版本号与最新的版本号一致,会修改成功,当前版本号+1;否则,就不会修改,对提交回滚。

CAS

CAS全称为Compare And Swap,表示比较和交换。涉及内存值V,预期值A和修改值B。简单说,就是只有当预期值A与内存值V相等的时候,将V修改为值B。底层的CAS操作本身是原子性的,因此不会存在多线程安全的问题。也就是说,这与上锁所达到的效果一致。

超卖问题

用CAS乐观锁思想去解决超卖问题:
内存值为当前库存stock、期望为stock>0,修改后的值为stock-1。也是一个比较和交换的操作:先比较库存是否大于零,如果库存大于零,那么就将库存减一。
上述过程,纯正的CAS函数,都只是实现乐观锁的不同方式。同时,“比较和交换”需要具有原子性。但是想要实现原子性,又得加锁,或者通过MySQL中的一条指令、Redis中的Lua脚本来实现。

总结

对比直接使用悲观锁,比如,将“比较和交换”这一部分代码上锁,作为临界区,使多线程互斥访问(这段代码相当于具有了原子性)。其所能达到的效果,也是等同于CAS的。而CAS能够实现无锁编程,是依赖于原子性的。也就是说,基于CAS思想实现的乐观锁(不是基于版本号机制),和悲观锁很像,都是对临界区代码实现了原子性。因此对于这些不同的实现方式,关注点应该放到,实现原子性所付出的代价,比如:

  • 底层的CAS函数,就是一条CPU指令,自然是原子性的
  • 使用互斥锁,只有一个线程可以访问临界区,其他线程阻塞
  • 在Redis中利用Lua脚本实现

你可能感兴趣的:(java)