乐观锁和悲观锁,可重入锁和不可重入锁(1)

乐观锁和悲观锁,可重入锁和不可重入锁(1)


前言

感觉有一段时间没有写博客了呢。还是再接再厉吧,适当程度的总结能让我自己能够更加深入地巩固和理解自己所学习的一切。

还有,我很懒,而且我还是比较喜欢写日记的,所以常常,我就干脆把日记混合到博客里面了。

其实这四个概念真的还挺简单的。一切东西,关键还是在于如何去使用它们,我记得我初中的时候,科学课本上就已经有了黑洞的概念,而且当时老师给我们的感觉就是已经确定黑洞是实际存在的,但是直到2020年,貌似才真正证实了黑洞实际存在。道听途说,和事实可能会有一些差距,但是我实际上想说的,“很多时候,我们知道一件事情,和将其落实到实际上,这两点其实有着很大的差距。”

好了,不说废话了,我尽可能用尽可能短的文字来说清楚这四个概念之间的区别。


自勉

我,可能是你所见过最疯狂的人。


正文

这两个概念其实是描述锁的两个角度。

乐观锁和悲观锁是什么呢?我们其实都知道当给一个线程获取了一个代码块的锁的时候,其他的线程只能等待这个线程释放了锁之后才能拿到锁进入这个代码块执行对应的操作。

但是进程在等待获取到锁的时候其实什么事情都没法做,而且CPU时间片轮询机制下,从一个线程的上下文切换到另外一个线程的上下文的开销其实远远大于执行一条或者几条CPU指令的时间。

所以我们就想出了一种等效的方式来在一些特定的情况下避免synchronized字段的使用。这个方法就是乐观锁,之前使用synchronized字段强行让线程阻塞的方式其实就是与之相对的悲观锁机制。

悲观锁就悲观在其完全不相信多线程同时执行能让数据维持安全性,从而让特定线程在特定代码块只能通过串行的方式来进行,从而保证了数据的“绝对安全性”,虽然我从来不信所谓的“绝对”和所谓的“运气”。当然,因为让并行的程序强行编程串行,这毫无疑问会让效能大打折扣。

但是乐观锁就不一样了,乐观锁给予CPU所直接支持的一个命令,CAS(compare and swap),这个命令说明白点就是,我给出我的一个预期值。这个预期值往往是我之前从内存中读取时的值,如果我发现内存中的数据和我预期的数值相同。那么就乐观地认为在我对于数据的操作的同时,数据本身并没有发生变化,这样以来我就乐观地将自己修改后的数据更新到内存中。如果不同,那么将新的内存值读取到CPU中,重新执行对应的操作,操作完毕后再次更新数据。

但是乐观锁虽然高效,其实存在三个问题:
1. ABA问题:仅仅根据预期值是否变化来判定数据是否被改变,但是万一数据被改变后,又改了回来,我们无从知晓。
2. 多次执行问题:在一些情况下,可能会出现预期值太多次都不能很好地命中的问题,这样一来其实CAS的优势并不存在
3. 操作对象必须为原子数据的问题

那么这三个问题怎么解决呢?其实多次执行的问题,我们目前并没有办法解决,只能按照实际情况如果发现,命中几率实在太低。那么我们也只能退回到悲观锁来达成同步机制。

ABA问题的解决即是对实例添加一个版本戳,也就是对于该实例的每一次修改,都更新这个版本戳,从而能达到能不仅仅从实例数据有没有变化这一个方面来判断实例是否变化。Java中常见的例子是AtomicMarkableReference,AtomicStampedReference。

至于操作对象必须为原子化操作,这个属于待优化内容而并非问题,我们通过equals来判定实例本身是否发生过变化,如果发生变化,则视作为不命中,则重新再执行一次数据操作。这个对于实例比较大的情况下,命中率依然会一直降低。所以虽然乐观锁性能的确比悲观锁要好,但是悲观锁并非毫无用武之地,该需要老将出马还是需要老将出马的,毕竟老将虽然没有小将那样的活力,但是却有着小将所没有的经验!


后记

本来以为能够几句话把四个概念讲清楚的,但是万万没想到竟然光讲前面两个概念就洋洋洒洒写了这么多= =,只能分两个部分来讲了= =,剩下的半篇我明天会补上滴= =

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值