乐观锁与悲观锁

本文深入探讨了乐观锁和悲观锁的概念,包括其实现方式、优缺点以及应用场景。乐观锁利用版本号机制和CAS算法,在读多写少的情况下提高系统吞吐量;悲观锁在写操作频繁的场景下确保数据一致性,适用于冲突较多的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

乐观锁

总是假设最好的情况,每次使用数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在次期间别人有没有去更新这个数据,如果没有更新则可以进行更新操作,如果有更新可以不断的尝试(自旋)直到成功。

实现方式:版本号机制,CAS算法

版本号机制:在数据表中加一个版本号version字段,表示数据被修改的次数,数据每次被修改之后version+1.当线程要更新数据的时候,读取数据的同时也会读取version值,只有数据的version值和要更新字段的version值一致的时候才进行更新。

CAS算法:比较并交换,有三个操作数,需要操作的变量的内存值,进行比较的值,将要更新的值。当且仅当内存中的值和预期值相同的时候,才将该内存地址的值更新为目标值。若失败则自旋直到成功。Java中juc包下的原子类就是通过CAS算法实现的。CAS主要是通过Unsafe类来实现的。

CAS算法的缺点:

  1. ABA问题,AtomicStampedReference带有版本号的原子引用类解决这个问题。在进行CAS操作的时候,不仅要比较内存地址的值和预期值是否相等,还要比较版本号和预期版本号是否相等,若相等则修改变量的值及版本号,若不相等进行自旋操作。
  2. 长时间自旋的话CPU开销较大。
  3. 只能保证一个共享变量的原子操作,提供AtomicReference,可以将多个共享变量合并成一个变量然后用使用AtomicReference将其变成原子类。

悲观锁

总是假设最坏的情况,每次使用数据的时候别的线程会对数据进行修改,所以每次使用数据的时候都会上锁,这样别的线程想要获取数据的时候将会阻塞直到拿到锁。数据库中的行锁、表锁、读锁、写锁都属于悲观锁。Java中的synchornized和ReentrantLock都属于悲观锁。

应用场景

乐观锁使用于读多写少的情况,省去了锁的开销,加大了整个系统的吞吐量

悲观所适用于写比较多的情况,如果冲突发生比较多,乐观锁会一直自旋,降低系统的性能。

偏向锁/轻量级锁/重量级锁

偏向锁:当一个线程获取锁之后,不存在其它线程对这个锁的竞争,那么线程是不需要同步的,这种情况下该锁就被设置成偏向锁,线程不需要进行同步。当有其他线程来竞争这个锁的时候,将撤销偏向锁,升级为轻量级锁。

轻量级锁:使用CAS操作来获取锁,每个栈帧中会有锁记录,锁记录复制了对象头中的markwork。使用CAS获取锁,就是尝试将对象头中的markwork指向锁记录对应的地址,若成功,则表明获取该锁。若失败,自旋获取。当释放锁的时候将markwork指向对象头,如果成功则表明没有发生锁竞争,若失败,则表明存在锁竞争,膨胀为重量级锁。

重量级锁:轻量级锁解锁失败膨胀为重量级锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值