乐观锁与悲观锁

【什么是乐观锁与悲观锁?】
乐观锁:
乐观地看待事物,每次去取数据的时候都认为别人不会在此期间对数据进行修改,所以不上锁。但是在更新的时候会判断一下别人是否对数据改动过,若判断为改动过,则重新取数据、重新判断改动。若判断为未改动过,则更新数据。
其中,判断是否发生改动有两种实现方式:版本号机制CAS算法

悲观锁:
悲观地看待事物,每次去取数据的时候都认为别人会在此期间对数据进行修改,所以拿数据都会上锁。别人想拿到这个数据就会阻塞到他拿到锁。
传统数据库用到了很多悲观锁机制,如行锁,表锁,读锁,写锁,都是在操作之前先上锁。java中为保证线程安全的synchronized和ReentrantLock也是悲观锁的实现。

【乐观锁与悲观锁的应用场景】

乐观锁适用场景悲观锁适用场景
多读场景多写场景
当读写冲突发生的很少时,采用乐观锁可以省去加锁的开销,提高整个系统的效率。若冲突发生很多的时候,会使得不停地进行尝试,反而降低了效率当冲突发生的很多时,直接采用悲观锁,可以有效地保证线程安全,

【乐观锁的两种实现方式】

方式1:版本号机制
通常在数据表多加一列版本号(version)字段,用于表示数据被修改次数。
当数据被修改一次,version+1。当一条线程对数据进行更新时,在读取数据的同时也会读取其version,提交更新时,对比version是否相同。
若相同,则完成更新,反之,重新尝试更新操作,直至成功。

方式2:CAS算法
CAS:compare and swap
CAS算法实现了在不使用锁的情况下实现线程之间的变量同步。CAS涉及三个操作数:

  • 需要读写的内存值 V
  • 进行比较的值 A
  • 准备写入的新值 B

当V == A时,CAS通过原子方式用B替代V,否则不会执行任何操作(这种情况下,会再次尝试)

【乐观锁的缺点】

1.ABA问题:
如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个问题被称为CAS操作的 "ABA"问题。
解决方式:
JDK 1.5 以后的 AtomicStampedReference 类就提供了此种能力,其中的 compareAndSet 方法就是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。

2.循环时间长开销大

自旋CAS(也就是不成功就一直循环执行直到成功)如果长时间不成功,会给CPU带来非常大的执行开销。

3.只能保证一个共享变量的原子操作
CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效。
当有多个共享变量时,可以把多个变量放在一个对象里来进行 CAS 操作.所以我们可以使用锁或者利用AtomicReference类把多个共享变量合并成一个共享变量来操作。

参考资料,Snailclimb大神

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜鹊先生Richard

随缘~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值