五、原子操作(CAS)
原子操作(CAS)
一、CAS(Compare And Set)
Compare And Set(或Compare And Swap),CAS是解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS操作包含三个操作数——内存位置(V)、预期原值(A)、新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在CAS指令之前返回该位置的值。CAS有效地说明了“我认为位置V应该包含值A;如果包含该值,则将B放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。
在java中可以通过锁和循环CAS的方式来实现原子操作。Java中 java.util.concurrent.atomic包相关类就是 CAS的实现,atomic包里包括以下类:
| AtomicBoolean | 可以用原子方式更新的 boolean 值。 |
| AtomicInteger | 可以用原子方式更新的 int 值。 |
| AtomicIntegerArray | 可以用原子方式更新其元素的 int 数组。 |
| AtomicIntegerFieldUpdater | 基于反射的实用工具,可以对指定类的指定 volatile int 字段进行原子更新。 |
| AtomicLong | 可以用原子方式更新的 long 值。 |
| AtomicLongArray | 可以用原子方式更新其元素的 long 数组。 |
| AtomicLongFieldUpdater | 基于反射的实用工具,可以对指定类的指定 volatile long 字段进行原子更新。 |
| AtomicMarkableReference | AtomicMarkableReference 维护带有标记位的对象引用,可以原子方式对其进行更新。 |
| AtomicReference | 可以用原子方式更新的对象引用。 |
| AtomicReferenceArray | 可以用原子方式更新其元素的对象引用数组。 |
| AtomicReferenceFieldUpdater<T,V> | 基于反射的实用工具,可以对指定类的指定 volatile 字段进行原子更新。 |
| AtomicStampedReference | AtomicStampedReference 维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。 |
二、AtomicInteger
AtomicInteger可以用原子方式更新的 int 值。AtomicInteger 可用在应用程序中(如以原子方式增加的计数器),并且不能用于替换 Integer。但是,此类确实扩展了 Number,允许那些处理基于数字类的工具和实用工具进行统一访问。 我们拿 AtomicInteger为例来学习下 CAS操作是如何实现的。
通常情况下,在 Java中,i++等类似操作并不是线程安全的,因为 i++可分为三个独立的操作:获取变量当前值,为该值+1,然后写回新的值。在没有额外资源可以利用的情况下,只能使用加锁才能保证读-改-写这三个操作时“原子性”的。但是利用加锁的方式来实现该功能的话,代码将非常复杂及难以维护,如:
synchronized (lock) {
i++;
}
相关类中还需要增加 Object lock等额外标志,这样就带来了很多麻烦,增加了很多业务无关代码,给开发与维护带来了不便。
然而利用 atomic包中相关类型就可以很简单实现此操作,以下是一个计数程序实例:
public class Counter {
private AtomicInteger ai = new AtomicInteger();
private int i = 0;
public static void main(String[] args) {
final Counter cas = new Counter();
List<Thread> threads = new ArrayList<Thread>();
// 添加100个线程
for (int j = 0; j < 100; j++) {
threads.add(new Thread(new Runnable() {
public void run() {
// 执行100次计算,预期结果应该是10000
for (int i = 0; i < 100; i++) {
cas.count();
cas.safeCount();
}
}
}));
}
//开始执行
for (Thread t : threads) {
t.start();
}
// 等待所有线程执行完成
for (Thread t : threads) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("非线程安全计数结果:"+cas.i);
System.out.println("线程安全计数结果:"+cas.ai.get());
}
/** 使用CAS实现线程安全计数器 */
private void safeCount() {
for (;;) {
int i = ai.get();
// 如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值
boolean suc = ai.compareAndSet(i, ++i);
if (suc) {
break;
}
}
}
/** 非线程安全计数器 */
private void count() {
i++;
}
}
/**
非线程安全计数结果:9942
线程安全计数结果:10000
*/
其中非线程安全计数器所计算的结果每次都不相同且不正确,而线程安全计数器计算的结果每次都是正确的。
三、存在的问题
CAS虽然很高效的解决原子操作,但是CAS仍然存在三大问题:ABA问题、循环时间长开销大、只能保证一个共享变量的原子操作。
ABA问题:因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。 从Java1.5开始JDK的 atomic包里提供了一个类AtomicStampedReference 来解决ABA问题。这个类的 compareAndSet方法作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
循环时间长开销大:自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。如果JVM能支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用,第一它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation)而引起CPU流水线被清空(CPU pipeline flush),从而提高CPU的执行效率。
只能保证一个共享变量的原子操作:当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作。
五、原子操作(CAS)的更多相关文章
- java并发编程系列二:原子操作/CAS
什么是原子操作 不可被中断的一个或者一系列操作 实现原子操作的方式 Java可以通过锁和循环CAS的方式实现原子操作 CAS( Compare And Swap ) 为什么要有CAS? Compar ...
- Java并发编程(十一)——原子操作CAS
一.原子操作 syn基于阻塞的锁的机制,1.被阻塞的线程优先级很高,2.拿到锁的线程一直不释放锁怎么办?3.大量的竞争,消耗cpu,同时带来死锁或者其他安全. CAS的原理 CAS(Compare A ...
- java原子操作CAS
本次内容主要讲原子操作的概念.原子操作的实现方式.CAS的使用.原理.3大问题及其解决方案,最后还讲到了JDK中经常使用到的原子操作类. 1.什么是原子操作? 所谓原子操作是指不会被线程调度机制打断的 ...
- 原子操作cas
一.概念, 基于处理器指令,把比较和交换合成一个指令完成,保证了原子性: 因为是针对一个内存地址值的,一个内存地址指向一个变量,所以只对一个共享变量能保证原子性: 二.原子操作类 锁只有synchro ...
- hbase 原子操作cas
在高并发的情况下,对数据row1 column=cf1:qual1, timestamp=1, value=val1的插入或者更新可能会导致非预期的情况, 例如:原本客户端A需要在value=val ...
- 并发之ATOMIC原子操作--CAS乐观锁原理(二)
1.乐观锁介绍 程序完成并发操作时,访问数据时每次不加锁,假设没有冲突去完成某项操作,如果因为冲突失败就重试,直到成功为止.就是当去做某个修改或其他操作的时候它认为不会有其他线程来做同样的操作(竞争) ...
- CAS 原子操作
理会CAS和CAS: 有时候面试官面试问你的时候,会问,谈谈你对CAS的理解,这时应该有很多人,就会比较懵,当然,我也会比较懵,当然我和很多人的懵不同,很多人可能,并不知道CAS是一个什么东西,而在我 ...
- 并发编程--CAS自旋锁
在前两篇博客中我们介绍了并发编程--volatile应用与原理和并发编程--synchronized的实现原理(二),接下来我们介绍一下CAS自旋锁相关的知识. 一.自旋锁提出的背景 由于在多处理器系 ...
- 原子操作CAS-最小的线程安全
原文连接:(http://www.studyshare.cn/blog-front/blog/details/1166/0 )一.原子操作是什么? 如果有两个线程分别执行两个操作A和B,从第一个线程执 ...
- 原子操作&普通锁&读写锁
一:原子操作CAS(compare-and-swap) 原子操作分三步:读取addr的值,和old进行比较,如果相等,则将new赋值给*addr,他能保证这三步一起执行完成,叫原子操作也就是说它不能再 ...
随机推荐
- tomcat运行一段时间后报错"Too many open files"
tomcat运行一段时间后报打开太多文件错误:Too many open files 查看当前进程的文件打开数: lsof -n |awk '{print $2}'|sort|uniq -c |so ...
- 【IDEA】(2)---MAC代码模版
IDEA(2)-MAC代码模版 IDEA提供了许多的自带代码模版,这些模版主要是对于我们经常开发用到的代码制作成一个模版,比如for循环,这个是经常会用到的代码,如果没有代码模版,我们需要一个一个手动 ...
- PI对于两个SAP客户端通道的了解
你把你的报文放到ESR的MM里面试一下就知道了 日期格式之类的,可能有转换的你要输入2019-05-13这种 OA到PI不选,都是到一个系统,由PI再来分流 如果你要做成由OA来选的,就要参考类似于 ...
- Linux下载——下载文件的命令
Linux下载——获取网络文件的命令 摘抄:本文主要学习了在Linux系统中如何下载文件的命令. wget命令 wget命令是一个用来下载文件的命令,可以在后台运行,在用户退出之后仍能继续下载,支持代 ...
- 高强度学习训练第十一天总结:Class文件结构(二)
常量池 可以理解为Class文件之中的资源仓库,他是Class文件结构中与其他项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一 访问标志 在常量池结束后,紧接着的俩个字节代表访问标 ...
- FCC---Create Movement Using CSS Animation---设计一个盒子上下左右移动,结合animation, @keyframe, position (上下左右的offset)
When elements have a specified position, such as fixed or relative, the CSS offset properties right, ...
- canvas绘制流程图
最近在做一个需求,根据数据动态生成以下类似的流程图,需要可以设置每个节点的颜色,每个节点可添加点击移动等相关的事件 代码中有做很多的注释和说明,相关的文档说明链接:https://9eb75i.axs ...
- git终端提交代码
- 一文解读ITIL (转)
首先声明自己不是ITIL方面的专家,特别是具体的规范细节,后面论述如有不当,请指正.但我为什么会提起它?主要是因为它和运维(IT服务管理)相关性太大了.早起的运维完全就是以ITIL来蓝本构建的,在当时 ...
- MySQL基础之数据管理【3】
MySQL中的多表联查 --查询emp的id username age depName create table emp( id int unsigned auto_increment key, us ...