悲观锁:
总是假设最坏的情况,每次拿数据的时候都会上锁,适合写多的情况
传统关系型数据库用的比较多,比如行锁,表锁
java中synchronnized和ReentrantLock等独占锁就属于悲观锁
乐观锁:
与悲观锁相反,每次拿数据认为别人不会修改,所以不上锁,但还是会判断一下别人有没有更新数据,适合多读的情况
可以使用版本号机制和cas操纵实现乐观锁
java的原子类就是通过cas实现的
悲观锁适用与于多写,乐观锁适用于多读
版本号机制:
为数据添加version字段
线程读取数据时也会读取version值,数据被修改时version加1,只有提交的数据版本大于原数据版本才允许提交,否则重新进行操作
CAS:
cas的全称是Compare And Swap,即比较并交换。
将旧的预期值与内存中的值比较,如果旧的预期值与内存中的值相等,那么就将内存中的值设置为新值
当有多个线程对内存中的共享数据数据进行操作时,cas能保证只有一个线程能修改成功,原子类就是使用cas实现
java中通过unsafe类的方法实现cas操作,而unsafe类最终调用的是由c++实现的native方法
最终在最底层处理器上有两种方法实现cas:总线加锁和缓存加锁
总线加锁:cpu和内存之间交换数据是通过总线进行的,当一个处理器想要更新某个变量的值时,向总线发出LOCK信号,此时其他处理器的对该变量的操作请求将被阻塞,发出LOCK信号的处理器独占共享内存从而实现原子性
缓存加锁:cpu修改完变量后通过缓存一致性协议,通知其它处理器使其上的缓存失效并重新从主存读取,以此来保证原子性。
cas可能出现ABA问题,即数据从A变成B由变成A,数据实际被修改过,但cas认为没有被修改过,通过添加版本号即可解决(有的需求只要首尾一致就接受。但是有的需求SS看重过程,中间不能发生任何修改)
cas实际上是一种自旋锁,开销比较大