目录
悲观锁和乐观锁其实核心就一个是否支持多线程并发的问题。
1. 悲观锁
顾名思义就是很悲观,每次拿数据都会认为别的线程会修改该数据,所以会给数据上锁;
这样抢到锁的线程运行,取到数据做操作,
这期间其他线程想要访问该数据时,都是阻塞block挂起状态,操作不了;
核心就是不支持多并发,是单线程操作,通过抢占时间片的方式来抢锁的使用权,把并发变成了串行。
共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。
应用场景:
悲观锁适用于多写的场景,保证线程安全和数据安全
mysql的行锁、表锁、读锁、写锁;
java中的synchronized。
2. 乐观锁
顾名思义就是很乐观,每次拿数据都认为别的线程不会修改数据,因此不会给数据上锁;
但会在数据更新时判断一下,在此期间其他线程有没有对该数据做更新,最终通过多个线程的逐一更新获取数据的最终值;
判断单一线程操作数据期间,其他线程有没有对该数据做修改用的是,version版本号机制、CAS算法。
乐观锁支持多线程并发,每个线程在不同的时间节点对数据做更新操作,每次更新时候都会判断其他线程是否对数据做了更新。
(1)version版本号机制
一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。
当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。
sql源码:
update table set x=x+1, version=version+1 where id=#{id} and version=#{version};
(2)CAS算法机制
即compare and swap 或者 compare and set,涉及到三个操作数,数据所在的内存值,预期值,新值。当需要更新时,判断当前内存值与之前取到的值是否相等,若相等,则用新值更新,若失败则重试,一般情况下是一个自旋操作,即不断的重试。
应用场景:
乐观锁适用于多读的场景,获取数据不再创建、销毁锁,减少了锁的开销,加大了数据的吞吐量,
Redis等非关系型数据库
ps:Redis是单线程操作,把事务封闭在单一线程中,避免了线程的安全问题,所以里面没有加悲观锁;
不过对于依赖多个Redis操作的复合操作来说,还是需要加锁的,而且有可能是分布式锁,也可以用LUA脚本,用任务队列的方式解决多任务并发的问题。