并发编程-硬件加持的CAS操作够快么?
Talk is cheap
CAS(Compare And Swap),即比较并交换。是解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论位置V的值是否等于A, 都将返回V原有的值。
CAS的含义是”我认为V的值应该是A,如果是,那我将V的值更新为B,否则不修改并告诉V的值实际是多少“
Show you my code
在单线程环境中分别使用无锁,加锁以及cas进行十组5亿次累加运算,然后打印出平均耗时。
/**
* cas对比加锁测试
*
* @author Jann Lee
* @date 2019-11-21 0:12
**/
public class CasTest {
@Test
public void test() {
long times = 500_000_000;
// 记录耗时
List<Long> elapsedTime4NoLock = new ArrayList<>(10);
List<Long> elapsedTime4Synchronized = new ArrayList<>(10);
List<Long> elapsedTime4ReentrantLock = new ArrayList<>(10);
List<Long> elapsedTime4Cas = new ArrayList<>(10);
// 进行10组试验
for (int j = 0; j < 10; j++) {
// 无锁
long startTime = System.currentTimeMillis();
for (long i = 0; i < times; i++) {
}
long endTime = System.currentTimeMillis();
elapsedTime4NoLock.add(endTime - startTime);
// synchronized 关键字(隐式锁)
startTime = endTime;
for (long i = 0; i < times; ) {
i = addWithSynchronized(i);
}
endTime = System.currentTimeMillis();
elapsedTime4Synchronized.add(endTime - startTime);
// ReentrantLock 显式锁
startTime = endTime;
ReentrantLock lock = new ReentrantLock();
for (long i = 0; i < times; ) {
i = addWithReentrantLock(i, lock);
}
endTime = System.currentTimeMillis();
elapsedTime4ReentrantLock.add(endTime - startTime);
// cas(AtomicLong底层是用cas实现)
startTime = endTime;
AtomicLong atomicLong = new AtomicLong();
while (atomicLong.getAndIncrement() < times) {
}
endTime = System.currentTimeMillis();
elapsedTime4Cas.add(endTime - startTime);
}
System.out.println("无锁计算耗时: " + average(elapsedTime4NoLock) + "ms");
System.out.println("synchronized计算耗时: " + average(elapsedTime4Synchronized) + "ms");
System.out.println("ReentrantLock计算耗时: " + average(elapsedTime4ReentrantLock) + "ms");
System.out.println("cas计算耗时: " + average(elapsedTime4Cas) + "ms");
}
/**
* synchronized加锁
*/
private synchronized long addWithSynchronized(long i) {
i = i + 1;
return i;
}
/**
* ReentrantLock加锁
*/
private long addWithReentrantLock(long i, Lock lock) {
lock.lock();
i = i + 1;
lock.unlock();
return i;
}
/**
* 计算平均耗时
*/
private double average(Collection<Long> collection) {
return collection.stream().mapToLong(i -> i).average().orElse(0);
}
}
从案例中我们可能看出在单线程环境场景下cas的性能要高于锁相关的操作。当然,在竞争比较激烈的情况下性能可能会有所下降,因为要不断的重试和回退或者放弃操作,这也是CAS的一个缺点所在,因为这些重试,回退等操作通常用开发者来实现。
CAS的实现并非是简单的代码层面控制的,而是需要硬件的支持,因此在不同的体系架构之间执行的性能差异很大。但是一个很管用的经验法则是:在大多数处理器上,在无竞争的锁获取和释放的”快速代码路径“上的开销,大约是CAS开销的两倍。
为何CAS如此优秀
硬件加持,现代大多数处理器都从硬件层面通过一些列指令实现CompareAndSwap(比较并交换)同步原语,进而使操作系统和JVM可以直接使用这些指令实现锁和并发的数据结构。我们可以简单认为,CAS是将比较和交换合成是一个原子操作。
JVM对CAS的支持, 由于Java程序运行在JVM上,所以应对不同的硬件体系架构的处理则需要JVM来实现。在不支持CAS操作的硬件上,jvm将使用自旋锁来实现。
CAS的ABA问题
cas操作让我们减少了锁带来的性能损耗,同时也给我们带来了新的麻烦-ABA问题。
在线程A读取到x的值与执行CAS操作期间,线程B对x执行了两次修改,x的值从100变成200,然后再从200变回100;而后在线程A执行CAS操作过程中并未发现x发生过变化,成功修改了x的值。由于x的值100 ->200->100,所以称之为ABA的原因。
魔高一尺道高一丈,解决ABA的问题目前最常用的办法就是给数据加上“版本号”,每次修改数据时同时改变版本号即可。
Q&A
在竞争比较激烈的情况下,CAS要进行回退,重试等操作才能得到正确的结果,那么CAS一定比加锁性能要高吗?
并发编程-硬件加持的CAS操作够快么?的更多相关文章
- Netty的并发编程实践3:CAS指令和原子类
互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能的额外损耗,因此这种同步被称为阻塞同步,它属于一种悲观的并发策略,我们称之为悲观锁.随着硬件和操作系统指令集的发展和优化,产生了非阻塞同步,被称为 ...
- Java并发编程:什么是CAS?这回总算知道了
无锁的思想 众所周知,Java中对并发控制的最常见方法就是锁,锁能保证同一时刻只能有一个线程访问临界区的资源,从而实现线程安全.然而,锁虽然有效,但采用的是一种悲观的策略.它假设每一次对临界区资源的访 ...
- Java并发编程总结2——慎用CAS(转)
一.CAS和synchronized适用场景 1.对于资源竞争较少的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源:而CAS基于硬件实 ...
- Java并发编程总结2——慎用CAS
一.CAS和synchronized适用场景 1.对于资源竞争较少的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源:而CAS基于硬件实 ...
- java并发编程(十七)内存操作总结
转载请注明出处:http://blog.csdn.net/ns_code/article/details/17377197 主内存与工作内存 Java内存模型的主要目标是定义程序中各个变量的访问规则, ...
- 【Java并发编程】从CPU缓存模型到JMM来理解volatile关键字
目录 并发编程三大特性 原子性 可见性 有序性 CPU缓存模型是什么 高速缓存为何出现? 缓存一致性问题 如何解决缓存不一致 JMM内存模型是什么 JMM的规定 Java对三大特性的保证 原子性 可见 ...
- 并发编程CAS操作
并发编程CAS操作 简介 CAS即compare and swap,中文就是比较并交换 CAS是Java并发包的基石 原理 其实CAS的原理相对来说比较简单.将要被改变的数据和期望的值作比较,当两个值 ...
- 【Java并发编程实战】-----“J.U.C”:CAS操作
CAS,即Compare and Swap,中文翻译为"比较并交换". 对于JUC包中,CAS理论是实现整个java并发包的基石.从整体来看,concurrent包的实现示意图如下 ...
- Go并发编程之美-CAS操作
摘要: 一.前言 go语言类似Java JUC包也提供了一些列用于多线程之间进行同步的措施,比如低级的同步措施有 锁.CAS.原子变量操作类.相比Java来说go提供了独特的基于通道的同步措施.本节我 ...
随机推荐
- 不吹不黑也不撕,我们就简简单单谈谈Vue
Vue在近两年中得到了快速的发展,17年初开始,市场上对Vue开发者的需求量越来越大,北京在招的前端职位中40%的岗位对Vue技能有要求,在杭州,虽然React仍然是主力框架,但是Vue使用的比例也在 ...
- 运用第一性原理探寻AI本质
摘要:使用第一性原理探讨理解AI本质. 文章来源:宜信技术学院|宜信支付结算团队技术分享第二期-支付结算机器学习技术团队负责人 刘创 分享<AI与第一性原理> 分享者:宜信支付结算机器学习 ...
- 推荐几个IT交流社区
博客园,csdn,掘金,StackOverflow(境外),v2ex,开源中国,简书,头条
- C#基础操作符详解(上)
本节内容: 1.操作符概览: 2.操作符的本质: 3.操作符与运算顺序 4.操作符详解. 1.操作符概览: 操作符(Operator)也译为”运算符” 操作符是用来操作数据的,被操作符操作的数据称为操 ...
- Apache常见配置
一.yum安装与配置 1.1安装: [root@apache ~]# yum install http\* -y [root@apache ~]# echo "test01" ...
- Java自动化测试框架-04 - 来给你的测试报告化个妆整个形 - (上)(详细教程)
简介 前边通过宏哥的讲解和分享想必小伙伴们和童鞋们都已经见过testng框架生成的测试报告,是不是它的样子和长相实在是不敢让大家伙恭维.那么今天宏哥就当一回美容师,由宏哥来给它美美容:当一回外科医生, ...
- 设计模式(二十一)Proxy模式
在面向对象编程中,“本人”和“代理人”都是对象.如果“本人”对象太忙了,有些工作无法自己亲自完成,就将其交给“代理人”对象负责. 示例程序的类图. 示例程序的时序图.从这个时序图可以看出,直到调用pr ...
- C#基本网络操作
建档操作如ping,查询本机主机ip,同步异步查询局域网内主机,同步异步邮件发送等 1)ping 通过ping类测试网络 using System; using System.Text; using ...
- vue-music 跨域获取QQ音乐歌单接口
最近在看vue音乐APP视频学习,需要跨域获取歌单数据,视频中老师是在build/dev-server.js文件配置跨域接口的,但是新版的vue-cli是没有这个文件的,我的vue版本是"2 ...
- Excel在线预览(通过poi转html,含里面的图片)
支持03和07excel转html,直接上代码 测试类 /** * 主方法 * @author asus * */ public class App2 { public static void mai ...