五、原子操作(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,他能保证这三步一起执行完成,叫原子操作也就是说它不能再 ...
随机推荐
- MySQL基础之STRAIGHT JOIN用法简介
MySQL基础之STRAIGHT JOIN用法简介 引用mysql官方手册的说法: STRAIGHT_JOIN is similar to JOIN, except that the left tab ...
- Springboot整合Thmeleaf
1.概述 Thymeleaf类似JSP.Velocity.Freemarker都是模板引擎,主要用来展示数据,原理如下 springboot官网还是推荐使用Thymeleaf而不是jsp, 不使用js ...
- 【CodeChef】Find a special connected block - CONNECT(斯坦纳树)
[CodeChef]Find a special connected block - CONNECT(斯坦纳树) 题面 Vjudge 题解 还是一样的套路题,把每个数字映射到\([0,K)\)的整数, ...
- 朋友想玩下百度的ORC我鼓捣鼓捣thinkphp3集成百度sdk
他想玩的是文字识别 那就玩下 咱们开始 1 先到百度文字识别 添加个应用 这样就有了APPID API KEY SECRET KEY https://console.bce.baidu.com ...
- Flask笔记:信号机制
Flask中有内置的一些信号,也可以通过三方库blinker自定义信号,其实Flask内置的信号也是优先使用的blinker库,如果没有安装blinker才会使用自定义的信号机制.可以通过点击任意导入 ...
- Python--方法/技巧在哪用的典型例子
就我个人在学习Python的过程中,经常会出现学习了新方法后,如果隔上几天不用,就忘了的情况,或者刚学习的更好的方法没有得到应用,还是沿用已有的方法,这样很不利于学习和掌握新姿势,从而拉长学习时间,增 ...
- ThreadLocal(线程本地存储)
1. ThreadLocal,即线程本地变量或线程本地存储. threadlocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或组件之间一些公共变量传递的 ...
- Word模板注入攻击
Word模板注入攻击 0x00 工具准备 phishery:https://github.com/ryhanson/phishery/releases office版本:office 2010 0x0 ...
- gitlab设置项目组成员权限
你敢相信这是个码农? setting菜单的“Members”功能页: 该页面展示了当前Project的成员列表,以及每个成员对应的权限角色,Owner/Master/Developer 注意到该页面顶 ...
- Linux中的硬链接和软链接的概念、区别及用法
概念: 硬链接(hard link): A是B的硬链接(A和B都是文件名),则A的目录项中的inode节点号与B的目录项中的inode节点号相同,即一个inode节点对应两个不同的文件名,两个文件名指 ...