查看原文
其他

面试中的最常被问到的两种锁

Java极客技术 2019-12-20


点击上方“蓝字”

技术的故事还有很多  

只想与你 

静静分享  


N

公众号后台回复“java”,获得作者Java 知识体系/面试必看资料




之前在的文章中已经写了公平锁、非公平锁,独享锁、共享锁,互斥锁和读写锁,那么接下来我们就得介绍互乐观锁和悲观锁了。那我们我就来了解一波把!


锁的分类

1.公平锁/非公平锁

2.可重入锁
3.独享锁/共享锁
4.互斥锁/读写锁
5.乐观锁/悲观锁
6.分段锁
7.偏向锁/轻量级锁/重量级锁
8.自旋锁


乐观锁(Optimistic Locking)

所谓的乐观,实际上是相对于悲观锁来说,我们先看一下百度百科中的解释。

乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库 性能的大量开销, 特别是对长事务而言,这样的开销往往无法承受。相对悲观锁而言,乐观锁更倾向于开发运用。

上面的内容都是乐观锁在百度百科中的解释,我们如果想要理解这个锁,是需要我们去找一个场景来进行解释的。

我们就从最经典的案例“老王取钱”来说,


1图中有三个存在,分别表示老王,和老王账户,还有一个就是版本信息。版本信息默认是1,这时候老王要买点东西,结果发现钱不太够,那就去银行取点钱去呗,果断的来了银行。

然后告诉柜员,取5000块钱,然后柜员就会从他的账户余额里面扣除5000,就是-5000

这时候版本信息是1,但是我们对金额做了修改之后,要把版本信息换成2,因为现在保存的版本信息是1,柜员查看的时候也是1,老王取钱了, 就想着修改成2。

但是,就在这个时候,来事了,老王的媳妇出去买衣服,发现身上钱不太够,就打算取点钱,就来了另外一家银行,这时候老王钱拿到了,但是柜员还没修改版本信息

就是这样子的,

这时候告诉柜员要取钱,柜员就回去读卡了,发现版本信息是1,

然后就在这时候,老王这头,柜员打算把这-5000的操作记录到数据库中,然后把版本信息变成2,这时候校验数据库中的版本信息还是1,所以, 录入成功了,就稳定的把这个信息改成了2,这时候就是这样的

钱也到手了,老王美滋滋的拿钱走了。

然后在老王媳妇这边的柜员在操作的时候就会出现问题了,之前读出来的账户信息版本是1,但是他要去操作的时候,发现不对呀,有人修改过呀, 就会出现这种情况。

但是他想去修改的时候人家现在默认的是2,这时候他在比对的时候是1和2了,然后就想着在次提交,这时候,操作完成不了,这就太尴尬了

这种情况就是证明不能让老王媳妇这边的柜员,拿着一版本的数据去吧老王那边的数据覆盖掉。这种其实就相当于是一种乐观锁的提现。


上面的图解就是乐观锁,

乐观锁,大多是基于数据版本( Version )记录机制实现。 何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。 读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对, 如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。



那什么又是悲观锁呢?我们接下来在说说什么是悲观锁。

悲观锁

惯例,先来看看百度百科中的解释

悲观锁,正如其名,具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度, 因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性, 否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。


因为悲观锁总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。


案例来袭:“工资的那点事”

这天老王开工资了,工资已经到账了,这时候老王没开短信提醒,得去看看钱到还是没到?

然后就告诉柜员,帮我查查卡里有多少钱?

正在柜员查钱的时候,老王媳妇又来取钱了,上次买的衣服不好看,再买点,钱不够就来银行了。

这时候老王正在查钱,而悲观锁的意思就是我在读的时候,我是锁着的你是看不到的,可以这么理解。

这时候老王媳妇就处于等待的状态,这个样子就是相当于悲观锁。

因为悲观锁就是当我们去获取数据的时候,不论我们有没有打算去修改,悲观锁都会认为我们一定会去修改这个数据,所以 他会把这个数据直接锁死,其他的人想操作操作,那你就阻塞,直到轮到你获取锁为止。


悲观锁和乐观锁的区别也就在这里。

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


悲观锁总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等),当其他线程想要访问数据时,都需要阻塞挂起

关于乐观锁和悲观锁,你了解了么?



作者介绍:懿,一个被打击正在努力前进的码农

作者赞赏码




Java 极客技术公众号,是由一群热爱 Java 开发的技术人组建成立,专注分享原创、高质量的 Java 文章。如果您觉得我们的文章还不错,请帮忙赞赏、在看、转发支持,鼓励我们分享出更好的文章。




    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存