文档章节

JAVA并发编程CAS操作

草原-星空
 草原-星空
发布于 2017/08/02 23:05
字数 946
阅读 6
收藏 0

CAS操作 CAS是单词compare and set的缩写,意思是指在set之前先比较该值有没有变化,只有在没变的情况下才对其赋值。 我们常常做这样的操作

if(a==b) {  
    a++;  
}  

试想一下如果在做a++之前a的值被改变了怎么办?a++还执行吗?出现该问题的原因是在多线程环境下,a的值处于一种不定的状态。采用锁可以解决此类问题,但CAS也可以解决,而且可以不加锁。


int expect = a;  
if(a.compareAndSet(expect,a+1)) {  
    doSomeThing1();  
} else {  
    doSomeThing2();  
}  

这样如果a的值被改变了a++就不会被执行。 按照上面的写法,a!=expect之后,a++就不会被执行,如果我们还是想执行a++操作怎么办,没关系,可以采用while循环


while(true) {  
    int expect = a;  
    if (a.compareAndSet(expect, a + 1)) {  
        doSomeThing1();  
        return;  
    } else {  
        doSomeThing2();  
    }  
}  

采用上面的写法,在没有锁的情况下实现了a++操作,这实际上是一种非阻塞算法。

应用 Java.util.concurrent.atomic包中几乎大部分类都采用了CAS操作,以AtomicInteger为例,看看它几个主要方法的实现:

public final int getAndSet(int newValue) {  
    for (;;) {  
        int current = get();  
        if (compareAndSet(current, newValue))  
            return current;  
    }  
}

getAndSet方法JDK文档中的解释是:以原子方式设置为给定值,并返回旧值。原子方式体现在何处,就体现在compareAndSet上,看看compareAndSet是如何实现的:

public final boolean compareAndSet(int expect, int update) {  
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);  
}  

不出所料,它就是采用的Unsafe类的CAS操作完成的。 再来看看a++操作是如何实现的:

public final int getAndIncrement() {  
    for (;;) {  
        int current = get();  
        int next = current + 1;  
        if (compareAndSet(current, next))  
            return current;  
    }  
}  

几乎和最开始的实例一模一样,也是采用CAS操作来实现自增操作的。 ++a操作和a++操作类似,只不过返回结果不同罢了

public final int incrementAndGet() {  
    for (;;) {  
        int current = get();  
        int next = current + 1;  
        if (compareAndSet(current, next))  
            return next;  
    }  
}  

此外,java.util.concurrent.ConcurrentLinkedQueue类全是采用的非阻塞算法,里面没有使用任何锁,全是基于CAS操作实现的。CAS操作可以说是JAVA并发框架的基础,整个框架的设计都是基于CAS操作的。

缺点: 1、ABA问题 CAS操作容易导致ABA问题,也就是在做a++之间,a可能被多个线程修改过了,只不过回到了最初的值,这时CAS会认为a的值没有变。a在外面逛了一圈回来,你能保证它没有做任何坏事,不能!!也许它讨闲,把b的值减了一下,把c的值加了一下等等,更有甚者如果a是一个对象,这个对象有可能是新创建出来的,a是一个引用呢情况又如何,所以这里面还是存在着很多问题的,解决ABA问题的方法有很多,可以考虑增加一个修改计数,只有修改计数不变的且a值不变的情况下才做a++,也可以考虑引入版本号,当版本号相同时才做a++操作等,这和事务原子性处理有点类似! 2、比较花费CPU资源,即使没有任何争用也会做一些无用功。 3、会增加程序测试的复杂度,稍不注意就会出现问题。

总结: 可以用CAS在无锁的情况下实现原子操作,但要明确应用场合,非常简单的操作且又不想引入锁可以考虑使用CAS操作,当想要非阻塞地完成某一操作也可以考虑CAS。不推荐在复杂操作中引入CAS,会使程序可读性变差,且难以测试,同时会出现ABA问题。

© 著作权归作者所有

共有 人打赏支持
上一篇: Reactor,反应堆
下一篇: idea快捷键大全
草原-星空
粉丝 0
博文 31
码字总数 36144
作品 0
闸北
私信 提问
Java并发编程基础-锁-synchronized状态变换

synchoronized在Jdk1.6之前一直被称为重量锁,性能较差,很少被推荐使用。synchoronized在Jdk1.6中进行了大量优化,性能与JUC中的各种锁无差,也被逐渐的被大众所接受。 synchoronized将锁状...

black-star
09/15
0
0
Java并发编程学习五:Synchronized的锁优化以及CAS

从前几章的学习当中,我们知道了volidate只能保证可见性以及部分的原子性,而针对大部分的并发场景而言,部分的原子性是满足不了项目需求的,因此使用了锁机制或者原子类操作来满足我们的开发...

JerryLin123
11/22
0
0
Java Concurrency(二)——J.U.C atomic包源码解读

java5之后的java.util.concurrent包是世界级并发大师Doug Lea的作品,里面主要实现了 atomic包里Integer/Long对应的原子类,主要基于CAS; 一些同步子,包括Lock,CountDownLatch,Semaphore...

谦谦君子
2015/01/06
0
2
Java并发编程——由一个自旋锁介绍CAS操作

记得刚开始写java后端的时候,指导我的同事告诉我,作为一个后端开发,可读性在大部分情况下优于性能。可读性意味着我们要写出简单、清晰、易懂的代码。对于一个java后端开发者,这并不意味着...

黄炎dyn
06/03
0
0
Java 并发- Atomic类的深入分析

AtomicXXX类, 都是基本数据类型原子操作的封装类. 看源码AtomicXXX类, 实际上都需要native方法的支持, native方法则采用CAS算法, CAS算法是一种无锁算法,CAS是CPU支持的指令. 我的理解CAS是这...

JoyGao
2013/11/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Java 11 已发布,String 还能这样玩!

在文章《Java 11 正式发布,这 8 个逆天新特性教你写出更牛逼的代码》中,我有介绍到 Java 11 的八个新特性,其中关于 String 加强部分,我觉得有点意思,这里单独再拉出来讲。 Java 11 增加...

Java技术栈
14分钟前
0
0
什么是以太坊DAO?(二)

Decentralized Autonomous Organization,简称DAO,以太坊中重要的概念。一般翻译为去中心化的自治组织。 在上一节中,我们为了展示什么是DAO创建了一个合约,就像一个采用邀请制的俱乐部,会...

geek12345
29分钟前
4
0
全屋WiFi彻底无死角 这才是终极解决方案

无线网络现在不仅在家庭中不可或缺,在酒店、医院、学校等场景中的需求也越来越多。尤其是这些场景中,房间多但也需要每个房间都能够完美覆盖WiFi,传统的吸顶式AP就无法很好的解决问题。 H3...

linux-tao
43分钟前
4
0
Python日期字符串比较

需要用python的脚本来快速检测一个文件内的二个时间日期字符串的大小,其实实现很简单,首先一些基础的日期格式化知识如下 复制代码 %a星期的简写。如 星期三为Web %A星期的全写。如 星期三为...

dragon_tech
43分钟前
3
0
ORA 各种oraclesql错误

ORA-00001: 违反唯一约束条件 (.) ORA-00017: 请求会话以设置跟踪事件 ORA-00018: 超出最大会话数 ORA-00019: 超出最大会话许可数 ORA-00020: 超出最大进程数 () ORA-00021: 会话附属于其它某...

青峰Jun19er
47分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部