CAS(Compare And Swap),即比较并交换。是解决多线程并行状况下使用锁形成性能损耗的一种机制,CAS操做包含三个操做数——内存位置(V)、预期原值(A)和新值(B)。若是内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。不然,处理器不作任何操做。不管位置V的值是否等于A, 都将返回V原有的值。java
CAS的含义是”我认为V的值应该是A,若是是,那我将V的值更新为B,不然不修改并告诉V的值实际是多少“数据结构
在单线程环境中分别使用无锁,加锁以及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开销的两倍。并发
硬件加持,现代大多数处理器都从硬件层面经过一些列指令实现CompareAndSwap(比较并交换)同步原语,进而使操做系统和JVM能够直接使用这些指令实现锁和并发的数据结构。咱们能够简单认为,CAS是将比较和交换合成是一个原子操做。jvm
JVM对CAS的支持, 因为Java程序运行在JVM上,因此应对不一样的硬件体系架构的处理则须要JVM来实现。在不支持CAS操做的硬件上,jvm将使用自旋锁来实现。性能
cas操做让咱们减小了锁带来的性能损耗,同时也给咱们带来了新的麻烦-ABA问题。测试
在线程A读取到x的值与执行CAS操做期间,线程B对x执行了两次修改,x的值从100变成200,而后再从200变回100;然后在线程A执行CAS操做过程当中并未发现x发生过变化,成功修改了x的值。因为x的值100 ->200->100,因此称之为ABA的缘由。atom
魔高一尺道高一丈,解决ABA的问题目前最经常使用的办法就是给数据加上“版本号”,每次修改数据时同时改变版本号便可。
操作系统
在竞争比较激烈的状况下,CAS要进行回退,重试等操做才能获得正确的结果,那么CAS必定比加锁性能要高吗?