乐观锁CAS实现机制以及synchroized介绍

前言:

关于并发编程中的问题,几乎是面试必问的知识点,而关于锁也是花样百出的在进行各种变换的提问。比如最基本的就是乐观锁CAS以及优化机制和synchroized在新版jdk中的优化
在讲关于这些之前,我觉得有必要说一下乐观锁以及悲观锁的基本概念以及区别在哪里?

悲观锁:

悲观锁顾名思义,这个锁十分的悲观,当这个线程进入之后,总是认为会有其他线程会进入堆内存中拿到自己所操作的变量,造成并发问题的产生。所以每次都会都对对象进行上锁。但是这个上锁以及解锁的过程会耗费相当一部分资源以及时间。所以从性能上来说,在悲观锁并不是完美的解决方法。

乐观锁:

通过对前文的悲观锁的介绍,我们可以基本可以知道乐观锁的概念,同样顾名思义说的话,这个锁十分的乐观。每次进入线程都不会对对象进行上锁,而是在进行对值进行修改之前,先对原值进行查询,然后在进行更新操作,然后在进行更新操作。这个逻辑的sql代码如下:

update 表名 set column=newValue where id = n and columnName=oldValue

目前较为流行的乐观锁实现方法为CAS(compare and swap )意为比较和修改,从上我们可以看出这个逻辑可以保证并发安全。但是实际上真的这样吗?
以下就是著名的ABA问题。

ABA

在这里插入图片描述
1:整个流程如下,首先线程A对想要修改的值进行读取。
2:在还没有进行修改操作之前,线程B进行了读操作,然后在进行写操作时发现值没有改变,然后将值A修改为了值B
3:在线程B执行完之后,线程C进行了读取操作,然后对这个值进行了修改,修改时发现值依旧是值B,所以成功将值修改为了值A。
4:在线程B和C都执行完之后,此时线程A开始了写操作,但是在进行写入比对时,发现值依旧是A,所以将该值修改为了自己想要修改的值。

总结:从每个线程中的执行逻辑来看,三个线程的执行都是对的,但是问题就在于我们每个线程对其进行修改的操作对别的线程来说不一定都是可见的,如果有两个线程将你准备修改的值进行了修改,最后修改的结果和你之前的旧值,但是根据乐观锁的定义可知,这个值已经不是之前读取的那个值。

ABA问题的解决方法

目前较为流行的方法为版本号机制,即我们可以在数据库对应的表的字段中添加一个version字段中添加一个属性作为版本号,这个版本号默认值为1,对应的解释为该列的修改次数,即我们在进行修改操作之前,首先读取这列的version字段,然后进行写操作,在执行写操作时对版本号进行判断,判断版本号的数字是否与之前读取的大小一致,如果不一致,说明在这个期间有线程进入修改了值。则本次操作失败。然后重新读取值以及版本号进行下一轮的修改。

值得注意的是:在目前的mybatis-plus框架中集成了这个方法,我们只需要在对应的表中添加version字段,默认default值为1.然后在该实体类的属性的变量名上添加@Verison注解。

@Version
private Integer version;

然后进行组件的注册即可。

  @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }

悲观锁synchroized

在jdk6之前,synchroized被称为重量级锁,在很多非分布式的应用场景下面,他的性能并不是很令人满意。因为加锁解锁的过程往往占用了很多资源和时间,不过在jdk6之后oracle对synchroized进行了优化,即根据场景不同选择不同的上锁方案。在不满足锁方案的情况下,对锁进行升级。
1:如果是在单线程情况下,直接使用偏向锁
2:如果不是单线程情况下,优先使用CAS乐观锁
3:如果写入失败会短暂时间的循环。在一段时间后升级为synchroized重量级锁

所以在对synchroized进行了优化之后 ,使用synchroized关键字的性能并不会太低,能够适用于大部分的场景应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值