cas与乐观锁(jpa乐观锁)

独占锁是一种悲观锁,synchronized就是一种独占锁;它假设最坏的情况,并且只有在确保其它线程不会造成干扰的情况下执行,会导致其它所有需要锁的线程挂起直到持有锁的线程释放锁。所谓乐观锁就是每次不加锁,假设没有冲突而去完成某项操作;如果发生冲突了那就去重试,直到成功为止。CAS(CompareAndSwap)是一种有名的无锁算法。CAS算法是乐观锁的一种实现。CAS有3个操作数,内…

大家好,又见面了,我是你们的朋友全栈君。

独占锁是一种悲观锁,synchronized就是一种独占锁;它假设最坏的情况,并且只有在确保其它线程不会造成干扰的情况下执行,会导致其它所有需要锁的线程挂起直到持有锁的线程释放锁。

所谓乐观锁就是每次不加锁,假设没有冲突而去完成某项操作;如果发生冲突了那就去重试,直到成功为止。

CAS(Compare And Swap)是一种有名的无锁算法。CAS算法是乐观锁的一种实现。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B并返回true,否则返回false。

注:synchronized和ReentrantLock都是悲观锁。

注:什么时候使用悲观锁效率更高、什么使用使用乐观锁效率更高,要根据实际情况来判断选择。

什么是CAS机制

CAS是英文单词Compare And Swap的缩写,翻译过来就是比较并替换。

CAS机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。

更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。

CAS是英文单词Compare And Swap的缩写,翻译过来就是比较并替换。

CAS机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。

更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。

这样说或许有些抽象,我们来看一个例子:

1.在内存地址V当中,存储着值为10的变量。

cas与乐观锁(jpa乐观锁)

2.此时线程1想要把变量的值增加1。对线程1来说,旧的预期值A=10,要修改的新值B=11。

cas与乐观锁(jpa乐观锁)

3.在线程1要提交更新之前,另一个线程2抢先一步,把内存地址V中的变量值率先更新成了11。

cas与乐观锁(jpa乐观锁)

4.线程1开始提交更新,首先进行A和地址V的实际值比较(Compare),发现A不等于V的实际值,提交失败。

cas与乐观锁(jpa乐观锁)

5.线程1重新获取内存地址V的当前值,并重新计算想要修改的新值。此时对线程1来说,A=11,B=12。这个重新尝试的过程被称为自旋。

cas与乐观锁(jpa乐观锁)

6.这一次比较幸运,没有其他线程改变地址V的值。线程1进行Compare,发现A和地址V的实际值是相等的。

cas与乐观锁(jpa乐观锁)

7.线程1进行SWAP,把地址V的值替换为B,也就是12。

cas与乐观锁(jpa乐观锁)

从思想上来说,Synchronized属于悲观锁,悲观地认为程序中的并发情况严重,所以严防死守。CAS属于乐观锁,乐观地认为程序中的并发情况不那么严重,所以让线程不断去尝试更新。

CAS的优缺点:

乐观锁避免了悲观锁独占对象的现象,同时也提高了并发性能,乐观锁是对悲观锁的改进,虽然它也有缺点,但它确实已经成为提高并发性能的主要手段,而且jdk中的并发包也大量使用基于CAS的乐观锁。但它也有缺点,如下:

1.CPU可能开销较大
在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很大的压力。

2.不能保证代码块的原子性
CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用悲观锁了。

3.ABA问题。
CAS的核心思想是通过比对内存值与预期值是否一样而判断内存值是否被改过,但这个判断逻辑不严谨,假如内存值原来是A,后来被一条线程改为B,最后又被改成了A,则CAS认为此内存值并没有发生改变,但实际上是有被其他线程改过的,这种情况对依赖过程值的情景的运算结果影响很大。解决的思路是引入版本号,每次变量更新都把版本号加一。

ABA问题:

线程1准备用CAS修改变量值A,在此之前,其它线程将变量的值由A替换为B,又由B替换为A,然后线程1执行CAS时发现变量的值仍然为A,所以CAS成功。但实际上这时的现场已经和最初不同了。

 

cas与乐观锁(jpa乐观锁)

ABA问题处理:

思路:解决ABA最简单的方案就是给值加一个修改版本号,每次值变化,都会修改它版本号,CAS操作时都对比此版本号。

cas与乐观锁(jpa乐观锁)

JAVA中ABA中解决方案(AtomicStampedReference/AtomicMarkableReference)

AtomicStampedReference 本质是有一个int 值作为版本号,每次更改前先取到这个int值的版本号,等到修改的时候,比较当前版本号与当前线程持有的版本号是否一致,如果一致,则进行修改,并将版本号+1(当然加多少或减多少都是可以自己定义的),在zookeeper中保持数据的一致性也是用的这种方式;

AtomicMarkableReference则是将一个boolean值作是否有更改的标记,本质就是它的版本号只有两个,true和false,修改的时候在这两个版本号之间来回切换,这样做并不能解决ABA的问题,只是会降低ABA问题发生的几率而已;

例子:

private static AtomicStampedReference<Integer> atomicStampedRef =
        new AtomicStampedReference<>(1, 0);
public static void main(String[] args){
    Thread main = new Thread(() -> {
        System.out.println("操作线程" + Thread.currentThread() +",初始值 a = " + atomicStampedRef.getReference());
        int stamp = atomicStampedRef.getStamp(); //获取当前标识别
        try {
            Thread.sleep(1000); //等待1秒 ,以便让干扰线程执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        boolean isCASSuccess = atomicStampedRef.compareAndSet(1,2,stamp,stamp +1);  //此时expectedReference未发生改变,但是stamp已经被修改了,所以CAS失败
        System.out.println("操作线程" + Thread.currentThread() +",CAS操作结果: " + isCASSuccess);
    },"主操作线程");

    Thread other = new Thread(() -> {
        Thread.yield(); // 确保thread-main 优先执行
atomicStampedRef.compareAndSet(1,2,atomicStampedRef.getStamp(),atomicStampedRef.getStamp() +1);
        System.out.println("操作线程" + Thread.currentThread() +",【increment】 ,值 = "+ atomicStampedRef.getReference());
        atomicStampedRef.compareAndSet(2,1,atomicStampedRef.getStamp(),atomicStampedRef.getStamp() +1);
        System.out.println("操作线程" + Thread.currentThread() +",【decrement】 ,值 = "+ atomicStampedRef.getReference());
    },"干扰线程");

    main.start();
    other.start();
}

文章参考:https://www.jianshu.com/p/ae25eb3cfb5d

https://blog.csdn.net/justry_deng/article/details/83449038

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/129357.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • SPSS异方差检验的实现

    SPSS异方差检验的实现SPSS 异方差检验的实现此次介绍两种异方差检验的方法 残差图分析法和等级相关系数法残差图分析法当回归模型满足所有假定时 残差图上的 n 个点的散步应该是随机的 无任何规律 如果回归模型存在异方差性 残差图上的点散布会呈现一定的趋势 在 SPSS 中选择 转换 回归 线性 分别选入对应的自变量因变量 点击 保存 在残差栏中选择未标准化 确定 选择 图形 旧对话框 散点图 将未标准化的残差选入 X 轴 自变量选入 Y 轴点击 确定 得到残差图等级相关系数法计算残差步骤在 1 中已演示

    2025年6月23日
    0
  • Windows API 之 InternetOpen、InternetOpenUrl、InternetReadFile

    Windows API 之 InternetOpen、InternetOpenUrl、InternetReadFileInternetOpen:Initializesanapplication'suseoftheWinINetfunctions.HINTERNETInternetOpen(_

    2022年7月2日
    26
  • WinForm输入网址打开源码

    无聊练习一下WinForm,输入网址,点击按钮就在浏览器打开网址。源代码下载:http://hovertree.com/h/bjaf/cao15h74.htm

    2021年12月22日
    45
  • springboot事务的传播机制[通俗易懂]

    springboot事务的传播机制[通俗易懂]事务的传播机制所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。(,默认)REQUIRED :如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。SUPPORTS :如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。MANDATORY :如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。REQUIRES_NEW :创建一个新的事务,如果当前存在事务,则把当前事务挂起。

    2022年8月8日
    6
  • Don’t Panic! KRACK 没你想象的那么糟

    Don’t Panic! KRACK 没你想象的那么糟上海交通大学密码与计算机安全实验室(LoCCS)软件安全小组(GoSSIP)版权所有,转载请与作者取得联系!著名的计算机学术安全会议CCS在2017年录用了一篇名为KeyReinstallationAttacks:ForcingNonceReuseinWPA2的学术论文,看起来,这和其他的上百篇同样将要在11月1日在美国达拉斯喜来登酒店会议中心一起登场报告的论文没有太大的差…

    2022年5月2日
    38
  • 常见模拟电路设计 一(含仿真):方波、三角波、正弦波的互相发生「建议收藏」

    FPGA最近有些整累了,给大家开个模拟电路设计的坑,内含干货,请放心食用一、总体设计方案二、单元电路设计和原理说明2.1方波发生电路波形发生电路可以由集成运放芯片构成运算电路来实现。第一步的方波发生电路,可以由滞回比较器和RC电路构成,如图采用通用运放LM324芯片进行设计,C1和R1组成RC电路,而R2和R3以及LM324构成滞回比较器。D1、D2的作用是稳压。电路波形如下2.2三角波发生电路三角波发生器就是利用集成运放构成积分器,然后对方波信号进行运算,如图其中R4和C2的值

    2022年4月7日
    130

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号