悲观锁:比较悲观,认为线程安全问题一定会发生,因此在操作数据之前先获取锁,确保线程串行执行。-例如Synchronized、Lock都属于悲观锁。
乐观锁:比较乐观,认为线程安全问题不一定会发生,因此不加锁。只是在更新数据时去判断此期间有没有其它线程对这个数据做过修改。(其实依靠了数据库的锁)如果没有修改则认为是安全的,自己才更新数据。如果已经被其它线程修改说明发生了安全问题,此时可以重试或异常。
问题:乐观锁怎么知道在自己要更新数据,别的线程在此期间有没有对数据做了修改?
乐观锁的关键是判断之前查询得到的数据是否被修改过。
1.版本号法:
在基于数据库表的版本解决方案中,通常为数据库表增加一个“version”字段来记录数据的版本。
当读取数据时,会将当前的版本号一同读出;在更新数据时,会对版本号加一,并将其与数据库表对应记录的当前版本信息进行比较.(set version=version+1 where version=1)
2.CAS法(Compare and Set)
此方法是在版本号法的基础上进行了简化,版本号法判断数据是否被修改看得是version.而CAS法用要被修改的字段代替了version。这里以stock(库存)为例,先查询库存数,在修改的时候判断库存数是否和之前查询到的一致,一致就修改。
乐观锁的弊端:
假设一开始有10个线程都查询到了stock(库存)为100,但只有一个线程能执行修改操作把stock改为99,其他线程都会执行失败,这样看来乐观锁的失败率太高了,用户动不动就秒杀失败。。
这里因为是库存,即时数据变化了,也无所谓,优化为stock>0就可以了,所以即时数据在此期间被修改了,我们也可以修改。但有些情况下,这里的字段不是库存,我们必须通过数据在此期间有没有被修改过来判断,这种情况字段>0就行不通了。