乐观锁 订阅
乐观锁机制采取了更加宽松的加锁机制。相对悲观锁而言,乐观锁更倾向于开发运用。 展开全文
乐观锁机制采取了更加宽松的加锁机制。相对悲观锁而言,乐观锁更倾向于开发运用。
信息
应    用
金融行业
外文名
Optimistic locking
介    绍
记录机制
中文名
乐观锁
乐观锁乐观锁介绍
乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。而乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号等于数据库表当前版本号,则予以更新,否则认为是过期数据。
收起全文
精华内容
参与话题
问答
  • 悲观锁和乐观锁的使用

    万次阅读 多人点赞 2019-07-31 18:41:47
    1、悲观(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上,这样别人想拿这个数据就会block直到它拿到。传统的关系型数据库里边就用到了很多这种...

    1、悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
    最常用的就是 select … for update,它是一种行锁,会把select出来的结果行锁住,在本事务提交或者回滚之前,不允许其他事务对这些行做update、delete、for update操作。

    2、乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,期间该数据可以随便被其他人读取,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。
    版本号机制是乐观锁最常用的方式,就是在表中增加一个版本号的字段,更新前先查一遍获取版本号,再作为更新语句的where条件进行更新,如果数据在获取版本号之后,在更新之前已经改变了,那就会更新失败,因为最后更新了0条数据,java后台拿到更新数如果为0,则说明更新失败,出现了并发问题,然后做具体的处理。
    例如有两个人同时对某条数据做修改,过程如下:
    操作员A操作如下:
    select id, balance, version from table where id=“1”;
    查询结果:id=1, balance=1000, version=1

    update table set balance=balance+100, version=version+1 where id=“1” and version=1;
    执行后,返回的更新结果是1,说明更新了一条,数据库里的结果是:id=1, balance=1100, version=2

    操作员B操作如下:
    select id, balance, version from table where id=“1”;
    查询结果:id=1, balance=1000, version=1, 说明操作员A还没修改。

    update table set balance=balance-50, version=version+1 where id=“1” and version=1 ;
    查的时候,操作员A还没修改,当要更新时,操作员A已经先修改成功,所以数据库里实际值是id=1, balance=1100, version=2,
    操作员B也将版本号加一(version=2)试图向数据库提交数据(balance=950),但此时查不到where id=“1” and version=1 的数据,
    所以update就失败了,执行结果是0,说明没有对任何数据更新成功。

    现在再去查一下,结果还是操作员A操作完成之后的结果
    select id, balance, version from table where id=“1”;
    查询结果:id=1, balance=1100, version=2

    以上是自己实现版本号机制的原理,真正使用的版本号机制是数据库本身带有的机制,一旦发现更新的版本号不是最新的就会被驳回。

    展开全文
  • 面试必备之乐观锁与悲观锁

    万次阅读 多人点赞 2018-07-16 22:34:26
    乐观锁对应于生活中乐观的人总是想着事情往好的方向发展,悲观锁对应于生活中悲观的人总是想着事情往坏的方向发展。这两种人各有优缺点,不能不以场景而定说一种人好于另外一种人。 悲观锁 总是假设最坏的情况...

    本文已经收录进 Github 90k+ Star 的 Java项目 : https://github.com/Snailclimb/JavaGuide 。学 Java/准备 Java 面试 ,首选 JavaGuide。觉得不错的话,点个 star 哦!

    何谓悲观锁与乐观锁

    乐观锁对应于生活中乐观的人总是想着事情往好的方向发展,悲观锁对应于生活中悲观的人总是想着事情往坏的方向发展。这两种人各有优缺点,不能不以场景而定说一种人好于另外一种人。

    悲观锁

    总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronizedReentrantLock等独占锁就是悲观锁思想的实现。

    乐观锁

    总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

    两种锁的使用场景

    从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

    乐观锁常见的两种实现方式

    乐观锁一般会使用版本号机制或CAS算法实现。

    1. 版本号机制

    一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。

    举一个简单的例子:

    假设数据库中帐户信息表中有一个 version 字段,当前值为 1 ;而当前帐户余额字段( balance )为 $100 。当需要对账户信息表进行更新的时候,需要首先读取version字段。

    1. 操作员 A 此时将其读出( version=1 ),并从其帐户余额中扣除 $50( $100-$50 )。
    2. 在操作员 A 操作的过程中,操作员B 也读入此用户信息( version=1 ),并从其帐户余额中扣除 $20 ( $100-$20 )。
    3. 操作员 A 完成了修改工作,提交更新之前会先看数据库的版本和自己读取到的版本是否一致,一致的话,就会将数据版本号加1( version=2 ),连同帐户扣除后余额( balance=$50 ),提交至数据库更新,此时由于提交数据版本大于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2 。
    4. 操作员 B 完成了操作,提交更新之前会先看数据库的版本和自己读取到的版本是否一致,但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 2 ,而自己读取到的版本号为1 ,不满足 “ 当前最后更新的version与操作员第一次读取的版本号相等 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。

    这样,就避免了操作员 B 用基于 version=1 的旧数据修改的结果覆盖操作员A 的操作结果的可能。

    2. CAS算法

    compare and swap(比较与交换),是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。CAS算法涉及到三个操作数

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

    当且仅当 V 的值等于 A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试

    乐观锁的缺点

    ABA 问题是乐观锁一个常见的问题

    1 ABA 问题

    如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个问题被称为CAS操作的 "ABA"问题。

    JDK 1.5 以后的 AtomicStampedReference 类就提供了此种能力,其中的 compareAndSet 方法就是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。

    2 循环时间长开销大

    自旋CAS(也就是不成功就一直循环执行直到成功)如果长时间不成功,会给CPU带来非常大的执行开销。 如果JVM能支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用,第一它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation)而引起CPU流水线被清空(CPU pipeline flush),从而提高CPU的执行效率。

    3 只能保证一个共享变量的原子操作

    CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效。但是从 JDK 1.5开始,提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行 CAS 操作.所以我们可以使用锁或者利用AtomicReference类把多个共享变量合并成一个共享变量来操作。

    CAS与synchronized的使用情景

    简单的来说CAS适用于写比较少的情况下(多读场景,冲突一般较少),synchronized适用于写比较多的情况下(多写场景,冲突一般较多)

    1. 对于资源竞争较少(线程冲突较轻)的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。
    2. 对于资源竞争严重(线程冲突严重)的情况,CAS自旋的概率会比较大,从而浪费更多的CPU资源,效率低于synchronized。

    补充: Java并发编程这个领域中synchronized关键字一直都是元老级的角色,很久之前很多人都会称它为 “重量级锁” 。但是,在JavaSE 1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的 偏向锁轻量级锁 以及其它各种优化之后变得在某些情况下并不是那么重了。synchronized的底层实现主要依靠 Lock-Free 的队列,基本思路是 自旋后阻塞竞争切换后继续竞争锁稍微牺牲了公平性,但获得了高吞吐量。在线程冲突较少的情况下,可以获得和CAS类似的性能;而线程冲突严重的情况下,性能远高于CAS。

    优质原创PDF资源

    一些计算机基础+Java的原创PDF下载地址: https://pan.baidu.com/s/1H-tR39b9UYHPWRGm1V0-1Q 密码: 8djk

    展开全文
  • 很多东西都有点忘记了,不过回答总体是没大问题的

    前言

    关于线程安全一提到可能就是加锁,在面试中也是面试官百问不厌的考察点,往往能看出面试者的基本功和是否对线程安全有自己的思考。

    那锁本身是怎么去实现的呢?又有哪些加锁的方式呢?

    我今天就简单聊一下乐观锁和悲观锁,他们对应的实现 CAS ,Synchronized,ReentrantLock

    正文

    一个120斤一身黑的小伙子走了进来,看到他微微发福的面容,看来是最近疫情伙食好运动少的结果,他难道就是今天的面试官渣渣丙?

    等等难道是他?前几天刷B站看到的不会是他吧!!!

    是的我已经开始把面试系列做成视频了,以后会有各种级别的面试,从大学生到阿里P7+的面试,还有阿里,拼多多,美团,字节风格的面试我也都约好人了,就差时间了,大家可以去B站搜:三太子敖丙 观看

    我也不多跟你BB了,我们直接开始好不好,你能跟我聊一下CAS么?

    CAS(Compare And Swap 比较并且替换)是乐观锁的一种实现方式,是一种轻量级锁,JUC 中很多工具类的实现就是基于 CAS 的。

    CAS 是怎么实现线程安全的?

    线程在读取数据时不进行加锁,在准备写回数据时,先去查询原值,操作的时候比较原值是否修改,若未被其他线程修改则写回,若已被修改,则重新执行读取流程。

    举个栗子:现在一个线程要修改数据库的name,修改前我会先去数据库查name的值,发现name=“帅丙”,拿到值了,我们准备修改成name=“三歪”,在修改之前我们判断一下,原来的name是不是等于“帅丙”,如果被其他线程修改就会发现name不等于“帅丙”,我们就不进行操作,如果原来的值还是帅丙,我们就把name修改为“三歪”,至此,一个流程就结束了。

    有点懵?理一下停下来理一下思路。

    Tip:比较+更新 整体是一个原子操作,当然这个流程还是有问题的,我下面会提到。

    他是乐观锁的一种实现,就是说认为数据总是不会被更改,我是乐观的仔,每次我都觉得你不会渣我,差不多是这个意思。

    你这个栗子不错,他存在什么问题呢?

    有,当然是有问题的,我也刚好想提到。

    你们看图发现没,要是结果一直就一直循环了,CUP开销是个问题,还有ABA问题和只能保证一个共享变量原子操作的问题。

    你能分别介绍一下么?

    好的,我先介绍一下ABA这个问题,直接口述可能有点抽象,我画图解释一下:

    看到问题所在没,我说一下顺序:

    1. 线程1读取了数据A
    2. 线程2读取了数据A
    3. 线程2通过CAS比较,发现值是A没错,可以把数据A改成数据B
    4. 线程3读取了数据B
    5. 线程3通过CAS比较,发现数据是B没错,可以把数据B改成了数据A
    6. 线程1通过CAS比较,发现数据还是A没变,就写成了自己要改的值

    懂了么,我尽可能的幼儿园化了,在这个过程中任何线程都没做错什么,但是值被改变了,线程1却没有办法发现,其实这样的情况出现对结果本身是没有什么影响的,但是我们还是要防范,怎么防范我下面会提到。

    循环时间长开销大的问题

    是因为CAS操作长时间不成功的话,会导致一直自旋,相当于死循环了,CPU的压力会很大。

    只能保证一个共享变量的原子操作

    CAS操作单个共享变量的时候可以保证原子的操作,多个变量就不行了,JDK 5之后 AtomicReference可以用来保证对象之间的原子性,就可以把多个对象放入CAS中操作。

    我还记得你之前说在JUC包下的原子类也是通过这个实现的,能举个栗子么?

    那我就拿AtomicInteger举例,他的自增函数incrementAndGet()就是这样实现的,其中就有大量循环判断的过程,直到符合条件才成功。

    大概意思就是循环判断给定偏移量是否等于内存中的偏移量,直到成功才退出,看到do while的循环没。

    乐观锁在项目开发中的实践,有么?

    有的就比如我们在很多订单表,流水表,为了防止并发问题,就会加入CAS的校验过程,保证了线程的安全,但是看场景使用,并不是适用所有场景,他的优点缺点都很明显。

    那开发过程中ABA你们是怎么保证的?

    加标志位,例如搞个自增的字段,操作一次就自增加一,或者搞个时间戳,比较时间戳的值。

    举个栗子:现在我们去要求操作数据库,根据CAS的原则我们本来只需要查询原本的值就好了,现在我们一同查出他的标志位版本字段vision。

    之前不能防止ABA的正常修改:

    update table set value = newValue where value = #{oldValue}
    //oldValue就是我们执行前查询出来的值 

    带版本号能防止ABA的修改:

    update table set value = newValue ,vision = vision + 1 where value = #{oldValue} and vision = #{vision} 
    // 判断原来的值和版本号是否匹配,中间有别的线程修改,值可能相等,但是版本号100%不一样

    除了版本号,像什么时间戳,还有JUC工具包里面也提供了这样的类,想要扩展的小伙伴可以去了解一下。

    聊一下悲观锁?

    悲观锁从宏观的角度讲就是,他是个渣男,你认为他每次都会渣你,所以你每次都提防着他。

    我们先聊下JVM层面的synchronized:

    synchronized加锁,synchronized 是最常用的线程同步手段之一,上面提到的CAS是乐观锁的实现,synchronized就是悲观锁了。

    它是如何保证同一时刻只有一个线程可以进入临界区呢?

    synchronized,代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类的其他同步方法),有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行。

    我分别从他对对象、方法和代码块三方面加锁,去介绍他怎么保证线程安全的:

    • synchronized 对对象进行加锁,在 JVM 中,对象在内存中分为三块区域:对象头(Header)、实例数据(Instance
      Data)和对齐填充(Padding)。

    • 对象头:我们以Hotspot虚拟机为例,Hotspot的对象头主要包括两部分数据:Mark Word(标记字段)、Klass Pointer(类型指针)。

      • Mark Word:默认存储对象的HashCode,分代年龄和锁标志位信息。它会根据对象的状态复用自己的存储空间,也就是说在运行期间Mark Word里存储的数据会随着锁标志位的变化而变化。
      • Klass Point:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

      你可以看到在对象头中保存了锁标志位和指向 monitor 对象的起始地址,如下图所示,右侧就是对象对应的 Monitor 对象。

      当 Monitor 被某个线程持有后,就会处于锁定状态,如图中的 Owner 部分,会指向持有 Monitor 对象的线程。

      另外 Monitor 中还有两个队列分别是EntryList和WaitList,主要是用来存放进入及等待获取锁的线程。

      如果线程进入,则得到当前对象锁,那么别的线程在该类所有对象上的任何操作都不能进行。

    在对象级使用锁通常是一种比较粗糙的方法,为什么要将整个对象都上锁,而不允许其他线程短暂地使用对象中其他同步方法来访问共享资源?

    如果一个对象拥有多个资源,就不需要只为了让一个线程使用其中一部分资源,就将所有线程都锁在外面。

    由于每个对象都有锁,可以如下所示使用虚拟对象来上锁:

    java class FineGrainLock{  MyMemberClassx,y;  Object xlock = new Object(), ylock = newObject();  public void foo(){   synchronized(xlock){   //accessxhere    }   //dosomethinghere-butdon'tusesharedresources    synchronized(ylock){    //accessyhere    }  } public void bar(){    synchronized(this){    //accessbothxandyhere  }  //dosomethinghere-butdon'tusesharedresources } }

    • synchronized 应用在方法上时,在字节码中是通过方法的 ACC_SYNCHRONIZED 标志来实现的。

      我反编译了一小段代码,我们可以看一下我加锁了一个方法,在字节码长啥样,flags字段瞩目:

      javascript synchronized void test(); descriptor: ()V flags: ACC_SYNCHRONIZED Code: stack=0, locals=1, args_size=1 0: return LineNumberTable: line 7: 0 LocalVariableTable: Start Length Slot Name Signature 0 1 0 this Ljvm/ClassCompile;

    反正其他线程进这个方法就看看是否有这个标志位,有就代表有别的仔拥有了他,你就别碰了。

  • synchronized 应用在同步块上时,在字节码中是通过 monitorenter 和 monitorexit 实现的。

    每个对象都会与一个monitor相关联,当某个monitor被拥有之后就会被锁住,当线程执行到monitorenter指令时,就会去尝试获得对应的monitor。

    步骤如下:

    1. 每个monitor维护着一个记录着拥有次数的计数器。未被拥有的monitor的该计数器为0,当一个线程获得monitor(执行monitorenter)后,该计数器自增变为 1 。
      • 当同一个线程再次获得该monitor的时候,计数器再次自增;
    • 当不同线程想要获得该monitor的时候,就会被阻塞。
    1. 当同一个线程释放 monitor(执行monitorexit指令)的时候,计数器再自减。

      当计数器为0的时候,monitor将被释放,其他线程便可以获得monitor。

      同样看一下反编译后的一段锁定代码块的结果:

      public void syncTask();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=3, locals=3, args_size=1
             0: aload_0
             1: dup
             2: astore_1
             3: monitorenter  //注意此处,进入同步方法
             4: aload_0
             5: dup
             6: getfield      #2             // Field i:I
             9: iconst_1
            10: iadd
            11: putfield      #2            // Field i:I
            14: aload_1
            15: monitorexit   //注意此处,退出同步方法
            16: goto          24
            19: astore_2
            20: aload_1
            21: monitorexit //注意此处,退出同步方法
            22: aload_2
          23: athrow
            24return
          Exception table:
          //省略其他字节码.......

    小结:

    同步方法和同步代码块底层都是通过monitor来实现同步的。

    两者的区别:同步方式是通过方法中的access_flags中设置ACC_SYNCHRONIZED标志来实现,同步代码块是通过monitorenter和monitorexit来实现。

    我们知道了每个对象都与一个monitor相关联,而monitor可以被线程拥有或释放。

    🐂,小伙子我只能说,你确实有点东西,以前我们一直锁synchronized是重量级的锁,为啥现在都不提了?

    在多线程并发编程中 synchronized 一直是元老级角色,很多人都会称呼它为重量级锁。

    但是,随着 Java SE 1.6 对 synchronized 进行了各种优化之后,有些情况下它就并不那么重,Java SE 1.6 中为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁。

    针对 synchronized 获取锁的方式,JVM 使用了锁升级的优化方式,就是先使用偏向锁优先同一线程然后再次获取锁,如果失败,就升级为 CAS 轻量级锁,如果失败就会短暂自旋,防止线程被系统挂起。最后如果以上都失败就升级为重量级锁。

    Tip:本来锁升级的过程我是搞了个贼详细贼复杂的图,但是我发现不便于理解,我就幼儿园化了,所以就有了个简单版本的,先看下复杂版本的:

    幼儿园版本:

    看到这你如果还想白嫖,我劝你善良,万水千山总是情,不要白嫖行不行?点个赞再走哈哈。

    对了锁只能升级,不能降级。

    还有其他的同步手段么?

    ReentrantLock但是在介绍这玩意之前,我觉得我有必要先介绍AQS(AbstractQueuedSynchronizer)。

    AQS:也就是队列同步器,这是实现 ReentrantLock 的基础。

    AQS 有一个 state 标记位,值为1 时表示有线程占用,其他线程需要进入到同步队列等待,同步队列是一个双向链表。

    当获得锁的线程需要等待某个条件时,会进入 condition 的等待队列,等待队列可以有多个。

    当 condition 条件满足时,线程会从等待队列重新进入同步队列进行获取锁的竞争。

    ReentrantLock 就是基于 AQS 实现的,如下图所示,ReentrantLock 内部有公平锁和非公平锁两种实现,差别就在于新来的线程是否比已经在同步队列中的等待线程更早获得锁。

    和 ReentrantLock 实现方式类似,Semaphore 也是基于 AQS 的,差别在于 ReentrantLock 是独占锁,Semaphore 是共享锁。

    从图中可以看到,ReentrantLock里面有一个内部类Sync,Sync继承AQS(AbstractQueuedSynchronizer),添加锁和释放锁的大部分操作实际上都是在Sync中实现的。

    它有公平锁FairSync和非公平锁NonfairSync两个子类。

    ReentrantLock默认使用非公平锁,也可以通过构造器来显示的指定使用公平锁。

    相关资料

    Tip:本来这一栏有很多我准备的资料的,但是都是外链,或者不合适的分享方式,博客的运营小姐姐提醒了我,所以大家去公众号回复【资料】好了。

    技术总结

    锁其实有很多,我这里只是简单的介绍了一下乐观锁和悲观锁,后面还会有,自旋锁,自适应自旋,公平锁,非公平锁,可重入(文中提到的都是可重入),不可重入锁,共享锁,排他锁等。

    多去了解他们的用法,多去深究他们的原理以及实现,我后面会持续更新多线程方面的知识点。

    Tip:这是我新的技术总结方式,以后会慢慢完善,如果被博客平台二压公众号回复【多线程】获取。

    参考:《Java高并发编程》、《美团技术团队锁的思考》、《拉钩张雷java32个考点》

    絮叨

    我不断的尝试新的文章风格,我也把絮叨环节放到了最后就是给大家一个好的阅读体验,有建议随时提哟,新的技术总结方式如何?

    还记得我帮公司内推么,我收到300封简历,但是我放到系统的只有13份,不是我想吐槽大家,是真的得用心点啊,叫我内推发个邮件,简历都不发什么鬼,简历总共才100个字又是什么鬼。。。

    周末出一期视频说一下简历的问题,真的为你们春招担心啊仔。。。。

    絮叨

    另外,敖丙把自己的面试文章整理成了一本电子书,共 1630页!目录如下

    现在免费送给大家,在我的公众号三太子敖丙回复 【888】 即可获取。

    我是敖丙,一个在互联网苟且偷生的程序员。

    你知道的越多,你不知道的越多人才们的 【三连】 就是丙丙创作的最大动力,我们下期见!

    注:如果本篇博客有任何错误和建议,欢迎人才们留言!


    文章持续更新,可以微信搜索「 三太子敖丙 」第一时间阅读,回复【资料】有我准备的一线大厂面试资料和简历模板,本文 GitHub https://github.com/JavaFamily 已经收录,有大厂面试完整考点,欢迎Star。

    展开全文
  • MybatisPlus--乐观锁

    万次阅读 2020-08-17 19:39:41
    添加乐观锁插件 创建一个MybatisPlusConfiguration配置类,代码如下: import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor; import org.springframework.context.annotation.Bean; ...

    添加乐观锁插件

    创建一个MybatisPlusConfiguration配置类,代码如下:
    import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class MybatisPlusConfiguration {
    
        @Bean
        public OptimisticLockerInterceptor optimisticLockerInterceptor(){
            return new OptimisticLockerInterceptor();
        }
    
    }
    

    给字段添加@Version注解

    @Version
    private long version;  // 版本
    

    支持的数据字段类型只有:int、Integer、long、Long、Date、Timestamp、LocalDateTime也就是数字和日期两种类型。

    然后在进行更新的时候这个字段的数据如果和数据库不一致,则更新失败。也就是说每一次更新都确保了数据的正确性,确保操作的数据是最新的然后更新的时候进行确认。

    本质上是在更新语句后面加了where version=数据库中的值也就体现了乐观锁的特性。

    update tbl_user set name = 'update',version = 3 where id = 100 and version = 2
    

    最后奉上MP官网的文档地址:乐观锁插件

    展开全文
  • 乐观锁

    千次阅读 2018-06-12 12:43:52
    乐观锁 概念 乐观锁假定当前事务操纵数据资源时,不会有其他事务同时访问该数据资源,能保证多个事务并发访问数据,又能防止第二类丢失更新问题。 HIbernate实现 在应用程序中,可以利用Hibernate提供的版本...
  • 乐观锁和悲观锁的区别

    万次阅读 2019-09-21 16:15:44
    如果你觉得文字太长,可以直接先看文末思维导图总结,小编已为你整理了作者的主要观点,供你...传统的关系型数据库里边就用到了很多这种机制,比如行锁,表锁等,读,写等,都是在做操作之前先上。它指的是...
  • 悲观锁和乐观锁的区别和应用场景

    万次阅读 2018-08-14 21:46:31
    悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上...乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,...
  • 乐观锁和悲观锁 首先我们理解下两种不同思路的锁,乐观锁和悲观锁。 这两种锁机制,是在多用户环境并发控制的两种所机制。下面看百度百科对乐观锁和悲观锁两种锁机制的定义: 乐观锁( Optimistic Locking ) ...
  • 乐观锁以及乐观锁的实现

    万次阅读 多人点赞 2018-08-10 16:38:45
    乐观锁介绍: 乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误...
  • Mysql共享锁、排他锁、悲观锁、乐观锁及其使用场景 一、相关名词 |--表级锁(锁定整个表) |--页级锁(锁定一页) |--行级锁(锁定一行) |--共享锁(S锁,MyISAM 叫做读锁) |--排他锁(X锁,MyISAM 叫做写锁...
  • 看网上说lock用的是悲观锁,但我们老师说reentrantlock用的乐观锁, reentrantlock实现lock接口,那按道理reentrantlock应该也是悲观锁啊,怎么可能是乐观锁呢? 那么,lock接口用的悲观锁还是乐观锁?...
  • 悲观锁:时刻保持着一个悲观的态度,对谁都不信任,总想着...乐观锁:相比于悲观锁,乐观锁对于这个世界都很乐观,对每个想要获取数据的操作,他都会认为大家都不会对自己的数据进行修改,所以不会上锁,在访问量巨...

空空如也

1 2 3 4 5 ... 20
收藏数 13,758
精华内容 5,503
关键字:

乐观锁