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

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • Linux上传下载命令_linux常用的20个命令

    Linux上传下载命令_linux常用的20个命令1、从服务器上下载文件scpusername@servername:/path/filename/var/www/local_dir(本地目录)例如scproot@192.168.0.101:/var/www/test.txt把192.168.0.101上的/var/www/test.txt的文件下载到/var/www/local_dir(本地目录)2、上传本地文件到服务器scp/p…

  • halcon手眼标定[通俗易懂]

    halcon手眼标定[通俗易懂]手眼标定最简单的方法:九点标定九点标定的目的:建立Mark点的图像坐标与其在机械手坐标系下的仿射变换矩阵,利用求得的仿射变换矩阵,可以把Mark点的图像坐标转成物理坐标,进而控制机械手完成定点抓取任务。halcon九点标定算子%Mark点像素坐标area_center(SortedRegions,Area,Row,Column)%Column_robot:=[275,225,17…

  • Qt的双缓冲技术(double buffering)

    Qt的双缓冲技术(double buffering)Qt的双缓冲技术(doublebuffering)是Qt绘画机制的一部分,是一种在Qt4中被全面采用的技术。其核心是:把一个窗口部件渲染到一个脱屏pixmap(off-screenpixmap)中,然后再把这个pixmap复制到显示屏幕上。这样做的目的是用于消除屏幕的闪烁并且因而界面会显得更漂亮。Qt4中,Qt会自动处理这些情况,所以在普通的绘画中,我们不必要关注这些内容。QT取消双

  • Windows XP虚拟机到期无法使用的解决方案(救急版)[通俗易懂]

    Windows XP虚拟机到期无法使用的解决方案(救急版)[通俗易懂]在之前的文章中已经教过大家如何在VMware中安装WindowsXP虚拟机,但是使用期限只有30天,近期有很多小伙伴说虚拟机到期了,开机之后显示无法登录,进不去桌面,更换密钥似乎也没成功,情况大概如下图这样:解决办法有两种:1.重新配置安装:VMware安装WindowsXP虚拟机并手动安装外加驱动程序但是这样有些麻烦,并且30天后还是会到期,所以推荐第二种办法。2.使用windowsXP的安全模式。…

  • icem二维非结构网格划分_Ansys Icem CFD网格划分实例详解PDF及附件[通俗易懂]

    icem二维非结构网格划分_Ansys Icem CFD网格划分实例详解PDF及附件[通俗易懂]网格划分是CFD仿真计算中重要的一环,icem也是Ansys家族的王牌成员,虽然现在已被mesh以及fluentmesh追赶,但是依旧是学生时代主流的追求完美结构化网格的利器!学好icem对于拓结构的理解也会更深刻!这本书没找到原版的,只有影印版,看起来不是很舒服,但是有配套视频可以学习,也算慰藉。建议买本纸质版康康,虽然纸质版也不是很清晰,因为不是彩印的,所以凑活看吧!一、书籍简介名…

  • 修改firefox地址栏的颜色

    修改firefox地址栏的颜色

发表回复

您的电子邮箱地址不会被公开。

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