并发编程-硬件加持的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提供了独特的基于通道的同步措施.本节我 ...
随机推荐
- 爬虫4:pdf页面+pdfminer模块+demo
本文介绍下pdf页面的爬取,需要借助pdfminer模块 demo一般流程: 1)设置url url = 'http://www.------' + '.PDF' 2)requests模块获取url ...
- webapck 按需加载及版本控制问题
在启用webpack的懒加载(按需加载)后,我们会遇到要解决缓存的问题. 解决缓存问题有几种方法: 第一种就是加个hash值.便每次修改后所编译后的文件名都不一样.这样能达到预期解决缓存的效果.具体设 ...
- Spring Boot 入门(七):集成 swagger2
本片文章是基于前一篇写的,<Spring Boot 入门(六):集成 treetable 和 zTree 实现树形图>,本篇主要介绍了spring boot集成swagger2.关于swa ...
- SDRAM
环境:STM32F769discovery板 SDRAM芯片:用的MT48LC4M32B2B5-6A 16Mbyte (sdram和flash一般标的容量都是bit) 硬件图: 第一步 配置FM ...
- [七年技术总结系列][理论篇]-RBAC权限模型由浅入深
权限部分将分两章介绍,第一章由浅入深介绍权限理论知识及应用,第二章介绍具体实现.后期再讲述中间件的使用时,还会插入一些权限内容,本质上属于中间件的应用. 权限模块是业务系统最常见.最基本的子集.本章假 ...
- ESP8266开发之旅 网络篇⑧ SmartConfig——一键配网
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- C#扩展一般用于linq
下面是dictionary的扩展 using System.Collections.Generic; namespace NetAnalysis.Common { public static clas ...
- 微服务架构 ------ Day01 微服务架构优缺点
1. 微服务架构的优点 庞大的单体程序 -> 一套微型程序. 每一个服务有明确的边界(服务之间的消息通讯机制) ,每一个服务都能单独的开发和维护,并且更好理解 每一个服务都能由一个团队来开发,当 ...
- kali更新源地址更改
问题: Hit:1 http://mirrors.ustc.edu.cn/kali kali-rolling InReleaseIgn:2 http://mirrors.ustc.edu.cn/kal ...
- Html.CSS.JavaScript 学习经验
HTML里面 不要使用 document.getElementsByName() 来获取 元素,会出错. 使用 document.getElementById()更好一些. substring()首字 ...