你真的了解乐观锁、悲观锁吗?

什么是悲观锁,什么是乐观锁;
官方解释:
悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。
乐观锁:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据
在这里插入图片描述
官方解释总是这么官方,看了也记不住,那么我们到底该如何了解呢?
悲观锁,比如数据库中的行锁(select xx from table1 where id =101 for update),表锁 (mysql低版本的默认引擎为MyISAM,在现在版本中默认的引擎都是innodb),再例如写文件就会有读锁和写锁,这些都是在操作之前先上锁,属于悲观锁。java中使用悲观锁的场景有synchronized和ReentrantLock。
乐观锁,大多数都体现在应用层面和高吞吐业务中,比较典型的案例就是版本号控制,在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS(compare and swap)实现的。
还不够清晰明了?下面我们来看看这个示例。
示例:一个程序中启动了5个线程,每个线程都让静态变量循环增加了100次。
最终输出的结果会一定是500么?
在这里插入图片描述
在这里插入图片描述
答案显然是否定的,因为线程安全的缘故,最终的结果可能会小于500!
不过我们可以加上同步锁之后看看效果
在这里插入图片描述
在这里插入图片描述
这是因为加上同步锁之后,自增操作变成了原子性操作,所以输出一定是500。
可是上文中中提及synchronized是悲观锁,在某些情况下并不是一个最优选择,在性能方面也需要考虑,一个程序的好坏,性能是对用户拥有最直观的感受。
这时候乐观锁就起到了决定性作用
在这里插入图片描述
在这里插入图片描述
Atomic操作类这么厉害?那是因为它的底层利用了CAS机制!
什么是CAS?
CAS,compare andswap(比较与交换),是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。CAS算法涉及到三个操作数
需要读写的内存值 V
进行比较的值 A
拟写入的新值 B
当且仅当 V的值等于 A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试。
CAS的缺点:
1,在并发量比较大的情况下,如果许多线程同时反复更新一个变量,但是又都更新不成功,一直循环会导致CPU的开销会很大。
2,CAS机制只能保证一个变量的原子性操作,而不能保证许多个变量同时进行原子性的更新,这时候就需要使用Synchronized了。
3,ABA问题,这也是CAS机制存在存在的最大问题。解释:
如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个问题被称为CAS操作的 “ABA”问题。
synchronized缺点:
1,在多线程竞争下,加锁、释放锁会导致较多的上下文切换和调度延时,引发性能问题。
2,一个线程持有锁会导致其他需要此锁的线程挂起
3,如果一个优先级高的线程等待一个优先级低的线程会导致优先级倒置,引起性能风险。
CAS与synchronized的使用情景
简单的来说CAS适用于写比较少的情况下(多读场景,冲突一般较少),synchronized适用于写比较多的情况下(多写场景,冲突一般较多)
对于资源竞争较少(线程冲突较轻)的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。
对于资源竞争严重(线程冲突严重)的情况,CAS自旋的概率会比较大,从而浪费更多的CPU资源,效率低于synchronized。
在前段时间中遇到的一个小问题(重点):
当某厂单据数量特别大,现场采用多人同时作业,导致更新数量时候出现并发,以至于待验量与已验量不符,形成验收失败。只有通过研发修复数据,现场对比数量来解决此类问题,耗时耗力。如何形成此种情况?是因为在更新已验量时并没有进行数据加锁。
如何解决?
在更新验收数量时先查询已验数量(RECEIVED_QTY),获取RECEIVED_QTY=100 ,更新时,调整update语句为 update received set RECEIVED_QTY=110 where … andRECEIVED_QTY=100

----------------------------------------END-----------------------------------------
喜欢本文的朋友,欢迎关注公众号【程序员阿宝】,查看更多精彩内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值