悲观锁和乐观锁详解

1.悲观锁

该锁的核心思想是总是假设最坏的情况,即每次取数据时都认为其他线程会修改该数据,所以每次在拿数据的时候都会上锁,这样其他人想拿这个数据的时候就会阻塞直到它拿到了锁;传统的关系型数据库中就有很多用到了这种锁机制,例如:表锁,行锁,读锁,写锁等,都是在操作之前先上锁。

下面大概介绍一下这四种锁的区别:

1.表锁(表级锁):开销小,加锁快;不会出现死锁;锁定力度大,发生锁冲突的概率最高,并发度最低。

2.行锁(行级锁):开销大,加锁慢;会出现死锁;锁定力度最小,发生锁冲突的概率最低,并发度最高。

3.读锁(共享锁):允许一个事务去读一行,阻止其他事务获得相同数据集的写锁;若事务T对数据对象A加上了读锁,那么事务T只能读A,但不能写或修改A;其他事务只能再对A加读锁,而不能加写锁,直到T释放A上的读锁。这保证了其他事务可以读A,但在T释放在A上的读锁之前不能对其进行修改;

4.写锁(排他锁):允许获取写锁的事务对其进行更新数据,并阻止其他事务获取对该事务的读锁和写锁;若事务T对数据对象A加上了写锁,则事务T可以对数据进行读和写的操作,其他事务不能对该数据对象进行读或写操作

2.乐观锁

该锁的核心思想是总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对该数据进行修改,因此不会对其上锁;但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号或者CAS操作实现;

1.version方式:一般是在数据库加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加1;当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值与当前数据库中的version值相等时才更新,否则重试更新操作直到更新成功。例如:updata student set name = "zhangsan",version = version + 1 where id = #{id} and version =  #{version};

缺点:CPU开销过大,因为如果有很多线程一直在反复尝试更新一个变量,却又不成功时,就会一直自旋消耗CPU;

2.CAS操作方式:即compare and swap,翻译过来就是比较并交换。维护三个变量值,一个是内存值V,一个是期望的旧的值A,一个是要更新的值B,更新一个变量的时候,只有当内存值与期望的旧的值相等时,才会执行更新操作,把内存V的值改为B;

缺点:1.CPU开销过大,因为如果有很多线程一直在反复尝试更新一个变量,却又不成功时,就会一直自旋消耗CPU;

           2.ABA问题。即有一个变量的值原来为A,然后一个线程先将变量的值修改为B然后另一个线程又将该变量的值修改为A,  此时普通的CAS会误判,误以为该变量没有变过;此时可以借助版本号来解决该问题;

注:我们常用的synchronized是悲观锁,lock是乐观锁 

Guess you like

Origin blog.csdn.net/wdyliuxingfeiyang/article/details/108347678