乐观锁与悲观锁

乐观锁和悲观锁是并发控制中的两种重要策略。乐观锁在读取数据时不加锁,但在更新时检查数据是否被修改,常通过CAS或版本号机制实现。适合读多写少的场景,提高读操作性能。悲观锁则在操作数据时加锁,确保数据一致性,适用于写操作频繁的情况,但可能导致阻塞和死锁。Java中的synchronized和ReentrantLock是悲观锁的实现例子。
摘要由CSDN通过智能技术生成

乐观锁

就是在操作数据时非常乐观,认为别的线程不会同时修改数据,所以不会上锁;但是在更新数据时会判断在此期间别的线程是否有更新过这个数据

大体流程

  1. 两个线程,如线程A、B直接获取同步数据,不会加锁,执行各自的操作
  2. 线程A、B在更新同步资源之前,都会判断资源是否被其他线程修改
  3. 如果同步资源没有被其他线程修改,那么直接更新内存中同步资源的值
  4. 如果同步资源被其他线程修改, 那么根据需要执行不同的操作,直接报错或者重试

实现

  • CAS实现

    例如Java中java.util.concurrent.atomic包下的原子变量使用了乐观锁的一种CAS实现方式

  • Version版本号机

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

      update user set name = "zs" ,version = oldVersion+1 where version = "oldVersion" 
      

总结

适合读操作多的场景,不加锁的特点能使其读操作的性能大幅提升


悲观锁

操作数据时很悲观,每次去拿数据时都认为别的线程会同时修改数据,所以每次在操作数据时都会上锁,这样别的线程想要操作这个数据时就会被阻塞直到它拿到锁

大体实现

  1. 多个线程,如线程A、B获取同步锁
  2. 假设线程A先加锁成功并执行对应的操作,那么线程B只能等待线程A释放锁之后才能操作,线程B处于阻塞状态
  3. 线程A释放同步锁,然后CPU会唤醒等待的线程,即线程B会再次尝试获取锁
  4. 线程B成功获取锁,再执行自己的操作

实现

  • 传统的关系型数据库使用这种锁机制,例如:行锁、表锁、读锁、写锁等,都是在操作之前先加锁
  • Java中的synchronize和ReentrantLock等独占锁

缺点

  • 需要阻塞,导致效率低下
  • 可能造成某个线程永久等待,即发生死锁的可能性较大

总结

适合并发写操作多的场景,先加锁再执行写操作,能保证写操作的数据正确性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值