我对悲观锁与乐观锁的一点理解

我对悲观锁与乐观锁的一点理解

说起到锁,我们都知道,加锁就意味这开销的加大,那什么时候需要加锁呢?

多个用户针对同一资源进行处理,此时就有可能出现并发问题。比如,常见的商城下单购物,扣减商品库存这一操作,在并发的情况下就有可能出现商品超卖的情况,而为了避免商品超卖这一情况,我们就需要考虑到在操作商品库存时加锁。

有一话来说就是:在并发的场景下,我们需要有序的更新某条记录时。

悲观锁与乐观锁到底是什么

悲观锁:

用一句开玩笑的话说就是总有刁民想害朕,总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,知道操作完成后才会释放锁。这样别人想拿这个数据就会阻塞直到它拿到锁。

传统的关系型数据库里边就用到了很多这种锁机制,比如行锁、表锁、读锁、写锁、排他锁等,都是在操作之前先上锁。在Java中synchronized和ReentrantLock等独占锁都是悲观锁机制的体现。

乐观锁

也就是说很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下,在此期间别人有没有去更新这个数据,如果别人修改了数据则放弃操作,否则执行操作。

这两种锁对又有哪些优劣呢

乐观锁

优势:

轻量级锁,避免了线程切换的开销

劣势:

  • 会有ABA问题

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

  • 只能对单一变量加锁

  • 自旋操作导致额外开销

悲观锁

优势:

可以锁住多个变量

劣势:

重量级锁,加锁、释放锁操作会增加开销,而且操作系统层面的上下文切换和线程调度也会引起很大的开销。

一个线程持有锁会导致其他需要此锁的线程挂起。

两种锁的使用场景

从上面的介绍,我们知道两种锁各有优缺点,需要根据场景进行对应的选择。像乐观锁适用于多读写少的情况下,这样可以省去了锁的开销,加大系统的整体吞吐量。但如果是多写的情况下,经常会产生冲突,会导致上层应用不断重试,这样反而降低了性能,所以一般多写的场景下用悲观锁就比较合适。

放在现实开发中,比如说同为商城系统,也可以根据用户量的不同,业务的不同,使用不同的加锁机制。合适的就是最好的。

这两种锁怎么去实现

乐观锁的实现主要方式有两种:CAS算法和版本号机制

版本号机制

一般是在数据表中加入一个版本号字段,标识数据被修改的次数,读取数据时,需要同时读取版本号,当数据修改时,需要判断版本号跟数据库中的是否一致,一致时才更新,版本号+1。如果不一致则执行重试更新操作。

CAS算法

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

  1. 需要读写的内存位置(V)
  2. 进行比较的预期值(A)
  3. 拟写入的新值(B)

操作逻辑如下:

如果内存位置V的值等于预期的A值,则将该位置更新为新值B,否则不进行任何操作。

许多CAS的操作是自旋的:如果操作不成功,会一直重试,直到操作成功为止。

这里引出一个新的问题,既然CAS包含了Compare和Swap两个操作,它又如何保证原子性呢?

答案是:CAS是由CPU支持的原子操作,其原子性是在硬件层面进行保证的。

悲观锁的两种实现:synchronized和数据库加锁

代码实现悲观锁:synchronized

synchronized通过对代码块加锁来保证线程安全:在同一时刻,只有一个线程可以执行代码块中的代码。

synchronized是一个重量级的操作,不仅是因为加锁需要消耗额外的资源,还因为线程状态的切换会涉及操作系统核心态和用户态的转换;

不过随着JVM对锁进行的一系列优化(如自旋锁、轻量级锁、锁粗化等),synchronized的性能表现已经越来越好。

数据库加锁

例:排他锁,select … for update

该查询语句会为改行记录加上排他锁,知道事务提交或回滚时才会释放排他锁。

在此期间,如果其他事务只能对改行记录执行查询操作。

注意:select … for update 一定要跟上where id = ? 的条件,id字段一定要是主键或者唯一索引,不然会导致锁全表。

最后

随着互联网的发展,三高架构的提出,悲观锁已经越来越少的使用到生产环境中了,尤其时是并发量比较大的业务场景。

所以对我们来说高并发环境下锁粒度把控是一门重要的学问,选择一个好的锁,在保证数据安全的情况下,可以大大提升吞吐率,进而提升性能。

一个很好的开源商城源码

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值