java并发编程:什么是cas?这回总算知道了(代码片段)

author author     2022-11-28     710

关键词:

无锁的思想

众所周知,Java中对并发控制的最常见方法就是锁,锁能保证同一时刻只能有一个线程访问临界区的资源,从而实现线程安全。然而,锁虽然有效,但采用的是一种悲观的策略。它假设每一次对临界区资源的访问都会发生冲突,当有一个线程访问资源,其他线程就必须等待,所以锁是会阻塞线程执行的。

当然,凡事都有两面,有悲观就会有乐观。而无锁就是一种乐观的策略,它假设线程对资源的访问是没有冲突的,同时所有的线程执行都不需要等待,可以持续执行。如果遇到冲突的话,就使用一种叫做CAS (比较交换) 的技术来鉴别线程冲突,如果检测到冲突发生,就重试当前操作到没有冲突为止。

CAS概述

CAS的全称是 Compare-and-Swap,也就是比较并交换,是并发编程中一种常用的算法。它包含了三个参数:V,A,B。

其中,V表示要读写的内存位置,A表示旧的预期值,B表示新值

CAS指令执行时,当且仅当V的值等于预期值A时,才会将V的值设为B,如果V和A不同,说明可能是其他线程做了更新,那么当前线程就什么都不做,最后,CAS返回的是V的真实值。

而在多线程的情况下,当多个线程同时使用CAS操作一个变量时,只有一个会成功并更新值,其余线程均会失败,但失败的线程不会被挂起,而是不断的再次循环重试。正是基于这样的原理,CAS即时没有使用锁,也能发现其他线程对当前线程的干扰,从而进行及时的处理。

CAS的应用类

Java中提供了一系列应用CAS操作的类,这些类位于java.util.concurrent.atomic包下,其中最常用的就是AtomicInteger,该类可以看做是实现了CAS操作的Integer,所以,下面我们就通过学习该类的案例来一窥全貌CAS的妙用。

学习AtomicInteger之前,我们先来看一段代码实例:

public class AtomicDemo

public static int NUMBER = 0;

public static void increase() 
    NUMBER++;


public static void main(String[] args) throws InterruptedException 
    AtomicDemo test = new AtomicDemo();
    for (int i = 0; i < 10; i++) 
        new Thread(() -> 
            for (int j = 0; j < 1000; j++)
                test.increase();
        ).start();
    
    Thread.sleep(200);
    System.out.println(test.NUMBER);


在main函数中开启了10个线程,执行后会轮流调用 increase(),当然我们知道,运行后输出的结果肯定不是我们期望的值,因为没有做线程安全的处理,所以10个线程流量操作临界区的资源NUMBER就会出错。

解决办法并不难,用我们之前学过的锁,例如synchronized修饰代码块,程序就会正常输出10000。当然,用锁解决并不是我们想要的方式,因为锁会阻塞线程,影响程序的性能,这时候,AtomicInteger就可以派上用场了。

将上面的程序改造一下,变成下面这样:

public static AtomicInteger NUMBER = new AtomicInteger(0);

public static void increase()
NUMBER.getAndIncrement();

public static void main(String[] args) throws InterruptedException
AtomicDemo test = new AtomicDemo();
for (int i = 0; i < 10; i++)
new Thread(() ->
for (int j = 0; j < 1000; j++)
test.increase();
).start();

Thread.sleep(200);
System.out.println(test.NUMBER);

运行main方法,程序输出的就是我们想要的值,也就是10000。

上面的代码中,increase方法里调用了NUMBER.getAndIncrement() ,这是AtomicInteger的自增方法,会对当前的值加1,并且返回旧值,点进方法的源码,它调用的是unsafe.getAndAddInt()方法:

public final int incrementAndGet()
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;

getAndAddInt的作用是对当前值加1,并返回旧值。

unsafe是Unsafe类的一个变量,通过Unsafe.getUnsafe()来获取

private static final Unsafe unsafe = Unsafe.getUnsafe();
Unsafe类是一个比较特殊的类,它是一个JDK内部使用的专属类,用一般的编辑器无法直接查看源码,只能看到反编译后的class文件。

这里要扩展一个知识点,就是Java本身无法访问操作系统,需要使用native方法,而Unsafe类中的方法就包含了大量的native方法,提高了Java对系统底层的原子操作能力。例如我们代码中使用到的getAndAddInt()底层就是调用一个native方法,用idea点击方法,得到下面反编译后的代码:

public final int getAndAddInt(Object var1, long var2, int var4)
int var5;
do
var5 = this.getIntVolatile(var1, var2);
while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

return var5;


public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
compareAndSwapInt的作用是比较并交换整数值,如果指定的字段的值等于期望值,也就是CAS中的 ‘A‘ (预期值),那么就会把它设置为新值 (CAS中的 ‘B‘),不难想象,该方法内部的实现必然是依靠原子操作完成的。除此之外,Unsafe类中还提供了其他的原子操作的方法,例如上面源码中的getIntVolatile就是使用volatile语义获得给定对象的值,这些方法通过底层的原子操作高效的提升了应用层面的性能。

CAS的缺点

虽然CAS的性能比起锁要强大很多,但它也存在一些缺点,例如:

1、循环的时间开销大

在getAndAddInt的方法中,我们可以看到,只是简单的设置一个值却调用了循环,如果CAS失败,会一直进行尝试。如果CAS长时间不成功,那么循环就会不停的跑,无疑会给系统造成很大的开销。

2、ABA问题

前面说过,CAS判断变量操作成功的条件是V的值和A是一致的,这个逻辑有个小小的缺陷,就是如果V的值一开始为A,在准备修改为新值前的期间曾经被改成了B,后来又被改回为A,经过两次的线程修改对象的值还是旧值,那么CAS操作就会误任务该变量从来没被修改过。这就是CAS中的“ABA”问题。

当然,"ABA"问题也有解决方案,Java并发包中提供了一个带有时间戳的对象引用 AtomicStampedReference,其内部不仅维护了一个对象值,还维护了一个时间戳,当AtomicStampedReference对应的数值被修改时,除了更新数据本身,还需要更新时间戳,只有对象值和时间戳都满足期望值,才能修改成功。这是AtomicStampedReference的几个有关时间戳信息的方法:

//比较设置 参数依次为:期望值 写入新值 期望时间戳 新时间戳
public boolean compareAndSet(V expectedReference, V newReference,
int expectedStamp, int newStamp)
//获得当前时间戳 欢迎工作一到五年的Java工程师朋友们加入Java程序员开发: 891219277

群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

public int getStamp()
//设置当前对象引用和时间戳
public void set(V newReference, int newStamp)

java并发编程之cas应用

...步组件中大量使用CAS技术鬼斧神工地实现了Java多线程的并发操作。整个AQS同步组件、Atomic原子类操作等等都是以CAS实现的。可以说CAS是整个J.U.C的基石。CAS比较交换的过程CAS(V, 查看详情

并发编程的灵魂:cas机制详解(代码片段)

...制存在的一些问题以及在Java中怎么使用CAS机制。其实Java并发框架的基石一共有两块,一块是本文介绍的CAS,另一块就是AQS,后续也会写文章介绍。什么是CAS机制CAS机制是一种数据更新的方式。在具体讲什么是CAS机制之前,我们... 查看详情

java并发编程的艺术

CAS有两个特点:  1、for循环  2、compareAndSet(可能别的线程先改变然后又重置,此时CAS是成功的,也就是CAS执行的过程中,可能多个线程对此变量做了修改,而不是各个线程互斥的修改)  3、CAS和互斥锁不... 查看详情

并发编程cas

首先搞明白什么是并发编程?并发编程是指在一台机器上同时处理多个任务。并发是指在同一个实体上的多个事件。多个事件在同一时间间隔发生。为什么需要并发编程(多线程编程)?有很多耗时的工作,如上传下载文件、与... 查看详情

go并发编程之美-cas操作

...来的开销,但是这是使用cpu资源做交换的。我简单列举了并发编程的大纲,需要详细的私信“555”~~ 查看详情

java并发编程面试题

   并发编程面试题-内存模型说下内存模型定义为什么要有内存模型为什么要重排序,重排序在什么时候排如何约束重排序规则happens-before什么是顺序一致性CAS实现的原理,是阻塞还是非阻塞方式?什么时候用,使用时... 查看详情

java并发编程之cas

CAS(Compareandswap)比较和替换是设计并发算法时用到的一种技术。简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替换当前变量的值。这听起来可... 查看详情

并发编程__原子变量

...法保证数据变量的原子性     CAS算法是硬件对于并发操作的支持     CAS包含了三个操作数:     ①内存值V     ②预估 查看详情

并发编程之深入理解cas

并发编程之深入理解CAS什么是CASCAS,compareandswap的缩写,中文翻译成比较并交换。CAS操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新... 查看详情

java并发编程系列-原子操作与cas

3.原子操作与CAS3.1原子操作所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何contextswitch,也就是切换到另一个线程。为了实现原子操作,Java中可以通过synchronized关键字... 查看详情

并发编程--cas自旋锁

在前两篇博客中我们介绍了并发编程--volatile应用与原理和并发编程--synchronized的实现原理(二),接下来我们介绍一下CAS自旋锁相关的知识。一、自旋锁提出的背景由于在多处理器系统环境中有些资源因为其有限性,有时需要互斥... 查看详情

java并发编程:并发编程基础各种锁详细介绍(代码片段)

转载自并发编程网–ifeve.com目录一、前言二、什么是线程安全问题三、什么是共享变量可见性问题四、原子性4.1介绍4.2原子变量类五CAS介绍六、什么是可重入锁七、Synchronized关键字7.1Synchronized介绍7.2Synchronized同步实例八、Reentrant... 查看详情

java并发编程:什么是线程安全,以及并发必须知道的几个概念(代码片段)

废话众所周知,在Java的知识体系中,并发编程是非常重要的一环,也是面试的必问题,一个好的Java程序员是必须对并发编程这块有所了解的。为了追求成为一个好的Java程序员,我决定从今天开始死磕Java的并发编程,尽量弥补... 查看详情

java并发编程之原子操作

  先来看一段简单的代码,稍微有点并发知识的都可以知道打印出结果必然是一个小于20000的值packagecom.example.test.cas;importjava.io.IOException;/***@authorhehangon2019-10-09*@description*/publicclassLockDemo{privatevolatileinti;publicvoidadd(){ 查看详情

java并发多线程编程——cas(代码片段)

...CAS缺点一、CAS的理解CAS的全称为Compare-And-Swap,它是一条CPU并发原语。CAS的功能是判断内存某个位置的值是否为预期值,如果是则更新为新的值,这个过程是原子的.二、CAS简单的代码示例示例代码importjava.util.concurrent.atomic.AtomicInteger;/... 查看详情

并发编程之cas的原理(代码片段)

什么是CASCAS(compareAndSwap),中文叫比较交换,一种无锁原子算法。过程是这样:它包含3个参数CAS(V,E,N),V表示要更新变量的值,E表示预期值,N表示新值。仅当V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明... 查看详情

java并发编程原理与实战四十三:cas----aba问题

...只有一个线程能够正确的修改数据,并且只修改一次。当并发的时候,其中一个线程已经将A成功的改成了B,但是在线程并发调度过程中尚未被调度,在这个期间,另外一个线程(不在并发中的请求线程)将B又修改成了A,那么原来... 查看详情

高并发编程之无锁(代码片段)

...的一些基础用法以及通过jvm内存模型的方式去介绍了一些并发中常见的问题(想看往期文章的小伙伴可以直接拉到文章最下方飞速前往)。本文重点介绍一个概念“无锁”本期精彩什么是无锁无锁类的原理AtomicIntegerUnsafeAto... 查看详情