,而在我看来我是不知道他问的是那个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无法感知,可以通过变量版本解决
3. 原子操作与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就无法保证操作的原子性,这个时候就可以用锁来保证原子性
AtomicInteger实现CAS 使用AtomicInteger、AtomicBoolean等原子操作类可以完成原子操作。 3. volatile + AtomicIntegerFieldUpdater实现CAS:netty中的应用 参考张龙netty教程的P.81~P.84 先说结论:volatile + AtomicIntegerFieldUpdater 也就是说,在每个用到CAS操作的整形值的地方,都需要维护一个AtomicInteger对象,也就是占用4+8=12字节。 只需要先分配一个静态变量,然后在每个用到CAS操作的整形值的地方,都只需要维护一个volatile int变量,也就是占用4字节。 而这两个函数是基于refCntUpdater对refCnt的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 操作的线程只会白白消耗处理器资源,而不会做任何有价值的工作,这就会带来性能的浪费。
CAS 操作包含三个操作数:内存位置(V)、预期值(A)、新值(B)。如果内存位置的值(V)与预期原值(A)相同,处理器会将该位置的值更新为新值(B)则 CAS 操作成功。 1 public class CAS { 2 public static void main(String[] args) { 3 //创建一个原子整数,当前值为默认值0 4 1 public final int getAndIncrement() { 2 return unsafe.getAndAddInt(this, valueOffset, 1); 3 } CAS 当对一个共享变量执行操作时,我们可以使用循环 CAS 的方式来保证原子操作,但是对多个共享变量操作时,循环 CAS 就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作 【3】ABA 问题:因为 CAS 需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用 CAS 进行检查时会发现它的值没有发生变化,但是实际上却变化了
CAS实现原子性操作 CAS操作大概有如下几步: 读取旧值为一个临时变量 对旧值的临时变量进行操作或者依赖旧值临时变量进行一些操作 判断旧值临时变量是不是等于旧值,等于则没被修改,那么新值写入.不等于则被修改 那么步骤三实际上就是比较并替换,这个操作需要是原子性的,不然无法保证比较操作之后还没写入之前有其他线程操作修改了旧值.那么这一步实际上就是CAS(CompareAndSwap),其是需要操作系统底层支持 + addValue; // 操作3,比较,如果旧值没改变则更新其为新值,否则重试.这种实现也被成为自旋CAS } while(! = 高效并发,那么CAS就是用来实现这个操作的原子性. 在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。
「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操作的弊端 我们可以设想一个场景:你要向银行卡中存入1000元钱,在存之前有2000,存之后应该是3000元。 带时间戳的CAS操作类AtomicStampedeReference 为了解决这种问题,JDK提供了一个带有时间戳的CAS操作类AtomicStampedeReference,它内部不仅维护了对象的值, public static void main(String[] args) { // 模拟多个线程同时更新后台数据库,为用户充值 for (int i=0;i<3;
SpringSecurity本身已经做好了与CAS的集成工作,只需要我们做简单配置就可以了 步骤1 spring-cas.xml配置文件内容如下(完整版) <? security" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w<em>3</em>. " /> <beans:property name="ticketValidator"> <beans:bean class="org.jasig.<em>cas</em>.client.validation.<em>Cas</em>20ServiceTicketValidator -- 注销客户端 --> <beans:bean id="singleLogoutFilter" class="org.jasig.<em>cas</em>.client.session.SingleSignOutFilter UserDetailsManager不需要改任何代码 @Service public class UserDetailsManager implements UserDetailsService { 步骤<em>3</em>
本节我们先来看看go中CAS操作 二、CAS操作 go中的Cas操作与java中类似,都是借用了CPU提供的原子性指令来实现。 CAS操作修改共享变量时候不需要对共享变量加锁,而是通过类似乐观锁的方式进行检查,本质还是不断的占用CPU 资源换取加锁带来的开销(比如上下文切换开销)。 ) //2.开启5个线程 for i := 0; i < threadNum; i++ { go incCounter(i) } //3. 这里之所以使用无限循环是因为在高并发下每个线程执行CAS并不是每次都成功,失败了的线程需要重写获取变量当前的值,然后重新执行CAS操作。 三、总结 go中CAS操作具有原子性,在解决多线程操作共享变量安全上可以有效的减少使用锁所带来的开销,但是这是使用cpu资源做交换的。
3.原子变量 CAS算法 前言 在上一篇中我们讲述了关于多线程并发,导致共享属性在内存不可见的问题。以及使用 volatile 关键字设置共享属性,使其在多线程并发中内存可见。 知识点说明 CAS 算法 - CAS (Compare-And-Swap) 是一种硬件对并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并发访问。 - CAS 包含了 3 个操作数: - 需要读写的内存值 V - 进行比较的值 A - 拟写入的新值 B - 当且仅当 V 的值等于 A 时,CAS 通过原子方式用新值 B 来更新 V CAS(Compare-And-Swap) 算法保证数据变量的原子性 * CAS 算法是硬件对于并发操作的支持 * CAS 包含了三个操作数: * ①内存值 V * ② 不管上面的操作如何,返回原来的内存值 V return oldValue; } //3.
本节我们先来看看go中CAS操作 二、CAS操作 go中的Cas操作与java中类似,都是借用了CPU提供的原子性指令来实现。 CAS操作修改共享变量时候不需要对共享变量加锁,而是通过类似乐观锁的方式进行检查,本质还是不断的占用CPU 资源换取加锁带来的开销(比如上下文切换开销)。 threadNum) //2.开启5个线程 for i := 0; i < threadNum; i++ { go incCounter(i) } //3. 这里之所以使用无限循环是因为在高并发下每个线程执行CAS并不是每次都成功,失败了的线程需要重写获取变量当前的值,然后重新执行CAS操作。 三、总结 go中CAS操作可以有效的减少使用锁所带来的开销,但是这是使用cpu资源做交换的。
CAS (Compare And Swap) CAS (Compare And Swap)是并发系统中,实现原子操作和锁的常见思想。 java 中,sun.misc.Unsafe 类提供了硬件级别的原子操作来实现 CAS,java.util.concurrent 包下的大量类都使用了这个 Unsafe.java 类的CAS操作。 打开 Unsafe 类的源码可以看到,大量的方法都是 native 方法,这是因为这个类是 jvm 通过 C++ 实现的硬件操作来保证其原子性的原子操作,这里就不对其实现多做介绍了。 3. (this, valueOffset, -1); } //当前值增加delta,返回旧值,底层CAS操作 public final int getAndAdd(int delta - 1; } //当前值增加delta,返回新值,底层CAS操作 public final int addAndGet(int delta) { return
乐观锁在Java中是通过无锁的方式实现的,典型的应用就是CAS算法。 ? 图2-13 乐观锁 根据从上面的概念描述我们可以发现: l 悲观锁适合写操作多的场景,先加锁可以保证写操作时数据正确。 l 乐观锁适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升。 2.5.2 CAS概念 CAS(比较与交换,Compare and swap)是一种有名的无锁算法。 CAS指令需要有3个操作数,分别是内存为止(在Java中可以简单理解为变量的内存地址,用V表示)、旧的预期值(用E表示)和新值(用N表示)。 实际上虚拟机采用CAS配合上失败重试的方式保证更新操作的原子性,原理和上面讲的一样。 2. TLAB 。 只有当缓冲区的内存用光需要重新分配内存的时候才会进行CAS操作分配更大的内存空间。
1 CAS原理 CAS是所有原子类的底层原理,乐观锁主要采用CAS算法。 CAS,比较并交换,是JDK提供的非阻塞原子性操作,通过硬件保证比较-更新操作的原子性。 CAS操作利用CPU的特殊指令,由CPU保证原子性,完成一系列操作,不存在安全性问题。 CAS的变量需要用volatile修饰,以便在各线程之间保证可见。 ObjectLocker ol(p, THREAD); if (*addr == e) { *addr = x; success = true; } return success; } UNSAFE_END 3 此处可能存在这样的情况,线程1获取变量值为5,线程2将值改为10,线程3再将值改回5。对于线程1,变量的值没有变,但对于计数等后续操作是不正确的。 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。 ?
而在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 适合简单操作;复杂操作建议使用锁机制