五、原子操作(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,他能保证这三步一起执行完成,叫原子操作也就是说它不能再 ...
随机推荐
- Oracle - 截取指定日期的alert log
工作中DBA经常会查看alert log来检查数据库后台都记录了些什么日志,如果只想看某一天或者某段时间范围的日志,能够把这些日志从大的alert log中截取下来放到一个单独的文件中,对于查看和下载 ...
- VMWare 虚拟机启动报“内部错误”的解决办法
情况 启动虚拟机的时候,启动不起来,弹出对话框,内部错误. 原因 Vmware 的 server 服务未开启. 解决办法 将以上服务都启动起来
- 解决No 'Access-Control-Allow-Origin' header is present on the requested resource.跨域问题
跨域错误 错误原因 解决方法在后台写一个过滤器过滤器来改写请求头头 CorsFilter.java public class CorsFilter implements Filter { @Overr ...
- Web前端—— JQuery迷你版实现以及使用
JQuery迷你版实现以及使用 tiny_jquery.js var $ = function (selector) { var ele = document.querySelector(select ...
- 用Python复制文件的9个方法
Python 中有许多"开盖即食"的模块(比如 os,subprocess 和 shutil)以支持文件 I/O 操作.在这篇文章中,你将会看到一些用 Python 实现文件复制的 ...
- SQL Server中的LEFT、RIGHT函数
SQL Server中的LEFT.RIGHT函数. LEFT:返回字符串中从左边开始指定个数字符. LEFT(character_expression,integer_expression); RIG ...
- vue浏览器全屏实现
1.项目中使用的是sreenfull插件,执行命令安装 npm install --save screenfull 2.安装好后,引入项目,用一个按钮进行控制即可,按钮方法如下: toggleFull ...
- Linux根目录下各目录含义
/boot:系统启动的相关文件,比如内核,grub /etc:配置文件 /dev:设备文件 /root:root用户的家目录 /home:用户家目录 /lib:库文件 /bin:用户的命令文件 /sb ...
- USB通信协议深入理解
0. 基本概念 一个[传输](控制.批量.中断.等时):由多个[事务]组成: 一个[事务](IN.OUT.SETUP):由一多个[Packet]组成. USB数据在[主机软件]与[USB设备特定的端点 ...
- Git 常用命令:
Git 常用命令: 1.git status #查看本地工作文件夹有哪些改动(和本地仓库对比),把差别列出. 2.git log #查看commit 的日志.(git log -2 只看最近2次的co ...