,而在我看来我是不知道他问的是那个CAS 我一般会问面试官,问他问的CAS是"原子操作",还是"单点登录" 因为在JAVA并发中的原子操作是称为CAS的,也就是英文单词CompareAndSwap CAS(Compare And Swap): 我们先要学习的是并发编程中的CAS,也就是原子操作 那么,什么是原子操作?如何实现原子操作? CAS以一种乐观锁的方式实现并发控制 如何实现原子操作: Java可以通过锁和循环CAS的方式实现原子操作 为什么要有CAS: CAS就是比较并且替换的一个原子操作,在CPU的指令级别上进行保证 (死)循环中[for(;;)]里不断进行CAS操作,直到成功为止(自旋操作即死循环) CAS问题: ABA问题: 那么什么是ABA问题? 只能保证一个共享变量的原子操作 当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法
CAS 我一般会问面试官,问他问的CAS是"原子操作",还是"单点登录" 因为在JAVA并发中的原子操作是称为CAS的,也就是英文单词CompareAndSwap的缩写,中文意思是:比较并替换。 CAS(Compare And Swap): 我们先要学习的是并发编程中的CAS,也就是原子操作 那么,什么是原子操作?如何实现原子操作? CAS以一种乐观锁的方式实现并发控制 如何实现原子操作: Java可以通过锁和循环CAS的方式实现原子操作 为什么要有CAS: CAS就是比较并且替换的一个原子操作,在CPU的指令级别上进行保证 (死)循环中[for(;;)]里不断进行CAS操作,直到成功为止(自旋操作即死循环) CAS问题: ABA问题: 那么什么是ABA问题? 只能保证一个共享变量的原子操作 当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法
CAS是CPU指令,CAS是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,那么就可以再次尝试 Go中使用CAS通常是结合for 无限循环 来实现原子化更新操作:for { // 假设data为共享变量,同一时刻可能有多个线程会更新它 old := data ok := atomic.CompareAndSwapInt64 (&data, old, new) if ok { return new }}CompareAndSwap会先进行比较,如果data的值等于old,那么就会执行替换操作并返回 true,如果不等于,则说明已经被其他线程操作了就返回false,所以它并不一定总能成功,尤其是在并发大的情况下,所以使用for循环来自旋。 造成很大的开销 一次只能保证一个共享变量的原子操作ABA问题-变量曾经被改过,CAS无法感知,可以通过变量版本解决
介绍 CAS技术是为了解决问题而生的,通过 CAS 我们可以以无锁的方式,保证对共享数据进行 “读取 - 修改 - 写回” 操作序列的正确性。CAS 是乐观锁设计思想的实现。 在大多数处理器上 CAS 都是非常轻量级的操作,这也是其优势所在。Java 的 CAS 操作CAS 依赖于 Unsafe 类提供的一些底层能力,进行底层操作。 目前 Java 提供了两种公共 API,可以实现 CAS 操作:一种是 Atomic 原子类。Atomic 包中的类对 Unsafe 类进行了封装,使我们可以更方便的使用 CAS 操作。 还有一种是 Variable Handle API,它源自于JEP 193,提供了各种粒度的原子或者有序性的操作等。CAS 的优劣局限CAS 的优点:在大多数处理器上 CAS 都是非常轻量级的操作。 如果有大量的线程同时对一个共享变量进行 CAS 操作,竞争过于激烈的情况下,尝试进行 CAS 操作的线程只会白白消耗处理器资源,而不会做任何有价值的工作,这就会带来性能的浪费。
AtomicInteger实现CAS 使用AtomicInteger、AtomicBoolean等原子操作类可以完成原子操作。 也就是说,在每个用到CAS操作的整形值的地方,都需要维护一个AtomicInteger对象,也就是占用4+8=12字节。 你只需要维护一个静态变量AtomicIntegerFieldUpdater,并为需要用到CAS操作的int值声明为volatile即可。 只需要先分配一个静态变量,然后在每个用到CAS操作的整形值的地方,都只需要维护一个volatile int变量,也就是占用4字节。 而这两个函数是基于refCntUpdater对refCnt的CAS操作。
CAS 操作包含三个操作数:内存位置(V)、预期值(A)、新值(B)。如果内存位置的值(V)与预期原值(A)相同,处理器会将该位置的值更新为新值(B)则 CAS 操作成功。 Unsafe 类存在于 sun.misc 包中,其内部方法操作可以像 C的指针一样直接操作内存,因为 Java 中 CAS 的操作依赖于 Unsafe 类的方法。 调用 Unsafe 类中的 CAS方法,JVM 会帮我们编译出 CAS汇编指令。这是一中完全依赖于硬件的功能,通过它实现原子操作。 当对一个共享变量执行操作时,我们可以使用循环 CAS 的方式来保证原子操作,但是对多个共享变量操作时,循环 CAS 就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作 比如有两个共享变量 i=2,j=a,合并一下 ij=2a,然后用CAS 来操作 ij。
CAS实现原子性操作 CAS操作大概有如下几步: 读取旧值为一个临时变量 对旧值的临时变量进行操作或者依赖旧值临时变量进行一些操作 判断旧值临时变量是不是等于旧值,等于则没被修改,那么新值写入.不等于则被修改 那么步骤三实际上就是比较并替换,这个操作需要是原子性的,不然无法保证比较操作之后还没写入之前有其他线程操作修改了旧值.那么这一步实际上就是CAS(CompareAndSwap),其是需要操作系统底层支持 ,对于操作系统会转换为一条指令,也就是自带原子性属性,对于Java中则是sun.misc.Unsafe#compareAndSwapObject这种类型的操作.另外在Java中CAS的实现需要可见性的支持 = 高效并发,那么CAS就是用来实现这个操作的原子性. CAS与乐观锁是什么关系? 乐观锁是一种思想,其认为冲突很少发生,因此只在最后写操作的时候加锁,这里的加锁不一定是真的锁上,比如CAS一般就用来实现这一层加锁.
「CAS」 操作有3个原子性操作: 读取内存的值 将内存的值与期望值比较 如果相等,则将内存值更新为新值 这三个操作一起完成,中间不会被线程切换打断。这就保证了比较和交换的原子性。 该方法尝试使用「CAS」操作更新obj的值,当且仅当obj的值等于expected时才更新,否则不做任何操作。 示例 C# 中提供了 Interlocked 类来实现 「CAS」 操作。 如果一个值从 A 改为 B,又改回 A,那么 「CAS」 操作会误认为值没有改变。常用的解决方法是使用版本号。 只能保证一个共享变量的原子操作。如果对多个共享变量操作,则需要使用锁。 资源浪费。 当 「CAS」 失败时,会进行重试,消耗 CPU 资源。 只能在某些平台使用。需要硬件对 「CAS」 操作的支持,一些低端硬件并不支持 「CAS」。 一般来说,当操作一个共享变量时使用 「CAS」,操作多个共享变量时使用锁可能更高效。如果硬件不支持 「CAS」,也只能使用锁。
于是JDK提供了一系列原子操作类:AtomicInteger、AtomicLong、AtomicBoolean、AtomicReference等,它们都是基于CAS去实现的,下面我们就来详细看一看原子操作类 CAS技术就是乐观锁的一种形式,Compare And Swap顾名思义比较交换,它会比较操作之前的值和预期的值是否一致,一致才进行操作,否则什么都不做,然后循环去CAS。 CAS的过程是这样的:它包含三个参数CAS(V,E,N)。V表示要更新的变量,E表示预期值,N表示新值。仅当V值等于E值时,才会对V的值设为N,否则当前线程什么都不做。 简单CAS操作的弊端 我们可以设想一个场景:你要向银行卡中存入1000元钱,在存之前有2000,存之后应该是3000元。 带时间戳的CAS操作类AtomicStampedeReference 为了解决这种问题,JDK提供了一个带有时间戳的CAS操作类AtomicStampedeReference,它内部不仅维护了对象的值,
本节我们先来看看go中CAS操作 二、CAS操作 go中的Cas操作与java中类似,都是借用了CPU提供的原子性指令来实现。 CAS操作修改共享变量时候不需要对共享变量加锁,而是通过类似乐观锁的方式进行检查,本质还是不断的占用CPU 资源换取加锁带来的开销(比如上下文切换开销)。 这里之所以使用无限循环是因为在高并发下每个线程执行CAS并不是每次都成功,失败了的线程需要重写获取变量当前的值,然后重新执行CAS操作。 读者可以把线程数改为10000或者更多会发现输出thread,5329,spinnum,1其中1说明该线程尝试了两个CAS操作,第二次才成功。 三、总结 go中CAS操作具有原子性,在解决多线程操作共享变量安全上可以有效的减少使用锁所带来的开销,但是这是使用cpu资源做交换的。
本节我们先来看看go中CAS操作 二、CAS操作 go中的Cas操作与java中类似,都是借用了CPU提供的原子性指令来实现。 CAS操作修改共享变量时候不需要对共享变量加锁,而是通过类似乐观锁的方式进行检查,本质还是不断的占用CPU 资源换取加锁带来的开销(比如上下文切换开销)。 这里之所以使用无限循环是因为在高并发下每个线程执行CAS并不是每次都成功,失败了的线程需要重写获取变量当前的值,然后重新执行CAS操作。 读者可以把线程数改为10000或者更多会发现输出thread,5329,spinnum,1其中1说明该线程尝试了两个CAS操作,第二次才成功。 三、总结 go中CAS操作可以有效的减少使用锁所带来的开销,但是这是使用cpu资源做交换的。
CAS (Compare And Swap) CAS (Compare And Swap)是并发系统中,实现原子操作和锁的常见思想。 java 中,sun.misc.Unsafe 类提供了硬件级别的原子操作来实现 CAS,java.util.concurrent 包下的大量类都使用了这个 Unsafe.java 类的CAS操作。 (this, valueOffset, -1); } //当前值增加delta,返回旧值,底层CAS操作 public final int getAndAdd(int delta ) { return unsafe.getAndAddInt(this, valueOffset, delta); } //当前值加1,返回新值,底层CAS操作 - 1; } //当前值增加delta,返回新值,底层CAS操作 public final int addAndGet(int delta) { return
原子操作与CAS 3.1 原子操作 所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何context switch,也就是切换到另一个线程。 JAVA内部在实现原子操作的类时都应用到了CAS。 3.2 CAS CAS是CompareAndSwap的缩写,即比较并替换。CAS需要有3个操作数:内存地址V,旧的预期值A,即将要更新的目标值B。 CAS指令执行时,当且仅当内存地址V的值与预期值A相等时,将内存地址V的值修改为B,否则就什么都不做。整个比较并替换的操作是一个原子操作,大多数现代处理器都支持CAS指令。 如果在这段期间它的值曾经被改成了B,后来又被改回为A,那CAS操作就会误认为它从来没有被改变过。这个漏洞称为CAS操作的“ABA”问题。 只能保证一个共享变量的原子操作 只能保证一个共享变量的原子操作:当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁来保证原子性
乐观锁在Java中是通过无锁的方式实现的,典型的应用就是CAS算法。 ? 图2-13 乐观锁 根据从上面的概念描述我们可以发现: l 悲观锁适合写操作多的场景,先加锁可以保证写操作时数据正确。 l 乐观锁适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升。 2.5.2 CAS概念 CAS(比较与交换,Compare and swap)是一种有名的无锁算法。 如果在这段期间它的值曾经改成了B,后来又改成了A,那么CAS操作就会误认为它没有改变过,这个漏洞称为“ABA”问题。 实际上虚拟机采用CAS配合上失败重试的方式保证更新操作的原子性,原理和上面讲的一样。 2. TLAB 。 只有当缓冲区的内存用光需要重新分配内存的时候才会进行CAS操作分配更大的内存空间。
1 CAS原理 CAS是所有原子类的底层原理,乐观锁主要采用CAS算法。 CAS,比较并交换,是JDK提供的非阻塞原子性操作,通过硬件保证比较-更新操作的原子性。 思想:获取当前变量最新值A(预期值),然后进行CAS操作。此时如果内存中变量的值V(内存值V)等于预期值A,说明没有被其他线程修改过,我就把变量值改成B(更新值);如果不是A,便不再修改。 CAS操作利用CPU的特殊指令,由CPU保证原子性,完成一系列操作,不存在安全性问题。 CAS的变量需要用volatile修饰,以便在各线程之间保证可见。 3.2 自旋时间长带来性能消耗 以AtomicLong为例,高并发场景下,如果线程一直无法进行CAS操作,内部是dowhile死循环,会一直自旋,消耗CPU。 4 Unsafe Unsafe是CAS核心类。Java无法直接访问底层操作系统,而是通过本地方法访问。JDK中Unsafe类,底层调用本地方法,提供硬件级别原子操作。
如上图所示,Unsafe 提供的 105 个 API 大致可分为内存操作、CAS、Class 相关、对象操作、线程调度、系统信息获取、内存屏障、数组操作等。 CAS 操作主要涉及到下面 3 个 API。 ? CAS 即比较并替换,实现并发算法时常用到的一种技术。CAS 操作包含三个操作数——内存位置、预期原值及新值。 执行 CAS 操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作。 的内存地址 valueAddress=“0x11000c”;然后通过 CAS 进行原子性的更新操作,成功则返回,否则继续重试,直到更新成功为止。 说完 CAS,我们再来说说 Unsafe 的内存操作。 内存操作主要有下面 9 个 API。 ?
主要原理 1 用户第一次访问一个CAS 服务的客户web 应用时(访问URL :http://192.168.1.90:8081/web1 ),部署在客户web 应用的cas AuthenticationFilter ,会截获此请求,生成service 参数 2 然后redirect 到CAS 服务的login 接口,url为https://cas:8443/cas/login? service=http%3A%2F%2F192.168.1.90%3A8081%2Fweb1%2F ,认证成功后,CAS 服务器会生成认证cookie ,写入浏览器,同时将cookie 缓存到服务器本地 web 应用时,AuthenticationFilter 在session 里读取不到用户信息,会去CAS 的login 接口认证,但这时CAS 会读取到浏览器传来的cookie ,所以CAS 不会要求用户去登录页面登录 cas原理流程图 发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/183895.html原文链接:https://javaforall.cn
而在Doug Lea提供的cucurenct包中,CAS理论是它实现整个java包的基石。 CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该 位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前 值。) 类似于 CAS 的指令允许算法执行读-修改-写操作,而无需害怕其他线程同时 修改变量,因为如果其他线程修改变量,那么 CAS 会检测它(并失败),算法 可以对该操作重新计算。 ,因为Java中CAS操作的助兴依赖于UNSafe类的方法。 3.3、不能保证多个共享变量的原子操作 问题描述:当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁来保证原子性
深度理解 CAS 原理:并发编程的原子操作基石在 Java 并发编程领域,CAS(Compare-And-Swap)是一种至关重要的原子操作机制,它是许多并发工具和数据结构的底层实现基础。 1.1 CAS 的基本操作CAS 操作包含三个核心参数:内存地址(V):需要操作的变量在内存中的地址预期值(A):线程认为变量当前应该具有的值新值(B):当变量值等于预期值时,线程希望将其更新为的值CAS ,这也是 CAS 操作的典型使用模式。 操作来保证线程安全,例如ConcurrentHashMap(JDK 8+):桶节点的插入和删除使用 CAS 操作扩容过程中通过 CAS 标记迁移状态计数操作使用LongAdder(基于 CAS 的累加器 5.1 CAS 的性能表现CAS 的性能优势并非绝对,其性能表现受多种因素影响:冲突率:冲突越少,CAS 性能越好;冲突频繁时,自旋重试会导致性能下降操作复杂度:CAS 适合简单操作;复杂操作建议使用锁机制
CAS应用 CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。 操作,每次从内存中读取数据然后将此数据和+1后的结果进行CAS操作,如果成功就返回结果,否则重试直到成功为止。 CAS缺点 CAS虽然很高效的解决原子操作,但是CAS仍然存在三大问题。ABA问题,循环时间长开销大和只能保证一个共享变量的原子操作 1. ABA问题。 当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作 比如有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。