面试必备:CAS无锁机制

CAS无锁机制原理,面试高频问题之一,其实,日常开发中并不会直接使用CAS无锁机制,都是通过一系列封装好的工具类来使用,

说不定面试官不提问,都不知道有这么个东西存在。

1、能说一下你对CAS的理解吗?

参考回答:

通常我们提到保证多线程安全,会想到三种方式,一是使用Synchronize关键字,但是有个问题就是,使用了Synchronize加锁后的多线程相当于串行,执行效率并不是太高,所以在高并发场景下,使用第二种方式Lock锁,Lock锁要比使用Synchronize关键字在性能上有极大的提高,其实Lock锁底层就是通过AQS+CAS机制实现的;第三种方式就是使用Java并发包下的Atomic「e淘米客」原子操作类,使用了原子类后就不需要使用Synchronize关键字或者是Lock加锁也是线程安全的,原子类的底层就是基于CAS无锁算法实现的。

CAS 比较后再交换。CAS有三个操作数,旧值A,新值B,以及需要读取的内存值V,在更新一个变量时,当且仅当A=V相同时,CAS才会将内存值V修改为B,否则什么都不做。

参考回答有点啰嗦了,提到了CAS的应用场景原子类的使用,但多说点显得有丶东西

2、能说一下CAS的实现原理吗?

参考回答

Java里的CAS是通过调用Unsafe「安sei夫」类的native方法,再由C调用CPU底层命令实现的。

如果问到Unsafe?

Unsafe 这个类是 JDK 提供的一个比较底层的类,是一个不安全的类,官方不建议开发者使用的,为啥呢?①Unsafe在未来可能被移除,也就是高版本jdk可能无法运行;②Unsafe中不少方法中必须提供内存地址和被替换对象的地址,中间会有一些计算问题,一旦出现问题就会导致jvm实例崩溃;③Unsafe中提供的直接访问内存的方法不受jvm管理,需要手动操作,如果疏忽可能会触发内存泄漏;

Unsafe通常用来:「额外补充,了解即可

  • 内存管理:包括分配内存、释放内存
  • 操作类、对象、变量:通过获取对象和变量偏移量直接修改数据
  • 挂起与恢复:将线程阻塞或者恢复阻塞状态
  • CAS:调用 CPU 的 CAS 指令进行比较和交换
  • 内存屏障:定义内存屏障,避免指令重排序

如果你想了解加深CAS原理,那么可以敲出一个原子类变量,比如AtomicInteger,直接看一下他的源码,找一下Unsafe是怎么大量应用的。

3、能说一下CAS的优缺点?

缺点

  1. CPU开销比较大
    在并发量比较高的情况下,如果线程反复尝试更新一个变量,却又一直更新不成功,循环往复,会给CPU带来很大的压力。
  2. 只能保证一个共享变量的原子操作
    只能是一个变量的原子操作,无法确保整个代码块的原子性,也就是在需要保证2个及以上变量共同进行原子性的更新时,就不得不使用Synchronized了
  3. ABA问题
    假设有一个变量 A ,经修改后变为B,然后又修改为 A,实际已经修改过了,但 CAS 可能无法感知,造成了不合理的值修改操作。
    解决方案是:使用版本号,在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A

*优点

在一般情况下,性能优先于锁的使用。

4、最后补充

从思想上来说,Synchronized属于悲观锁,悲观地认为程序中的并发情况严重,所以严防死守。

CAS属于乐观锁,乐观地认为程序中的并发情况不那么严重,所以让线程不断去尝试更新。

CAS和Synchronized没有绝对的好与坏,关键看使用场景。在并发量非常高的情况下,反而用同步锁更合适一些。

原文地址:https://www.cnblogs.com/niceyoo/p/12683341.html

时间: 2024-08-03 09:10:32

面试必备:CAS无锁机制的相关文章

面试必备之悲观锁与乐观锁

悲观锁 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程).传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁.Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现. 乐观锁 总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新

CAS无锁策略

在并发编程中,对于共享资源的使用需要确保绝对的安全性.除了利用锁机制之外,还有一种无锁的概念.所谓无锁,就是假定在并发情况下,对于共享资源的访问没有冲突,线程可以一直不停的运行,无需阻塞,如果产生冲突,则使用CAS算法确保安全性.Java在很多并发代码中都使用了这种算法. CAS算法的核心参数如下: compareAndSet(V,E,A) V代码需要进行更新的变量:E代表预期值:A代表所要更新的值. CAS的核心思想就是:当要对一个变量进行更新时,先取出该变量此时在内存中的实际值,与预期值进行

(转)面试必备之乐观锁与悲观锁

何谓悲观锁与乐观锁乐观锁对应于生活中乐观的人总是想着事情往好的方向发展,悲观锁对应于生活中悲观的人总是想着事情往坏的方向发展.这两种人各有优缺点,不能不以场景而定说一种人好于另外一种人. 悲观锁总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程).传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁

无锁机制实现并发访问

对于并发控制而言, 锁是一种悲观的策略.它总是假设每一次的临界区操作会产生冲突,因此,必须对每次操作都小心翼翼.如果有多个线程同时需要访问临界区资源,就宁可牺牲性能让线程进行等待,所以说锁会阻塞线程执行. 而无锁是一种乐观的策略,它会假设对资源的访问是没有冲突的.既然没有冲突,自然不需要等待,所以所有的线程都可以在不停顿的状态下持续执行.那遇到冲突怎么办呢?无锁的策略使用一种叫做比较交换的技术(CAS Compare And Swap)来鉴别线程冲突,一旦检测到冲突产生,就重试当前操作直到没有冲

无锁机制下的原子性操作

通常使用volatile关键字修饰字段可以实现多个线程的可见性和读写的原子性,但是对于字段的复杂性操作就需要使用synchronize关键字来进行,例如: public class Counter { private volatile int count = 0; public synchronized int getAndIncr() { return this.count ++; } } 这里可以看到,对于字段的简单设置和获取,volatile可以应付,但是我们想每次获取后自增加1,这样的操

CAS 无锁队列

队列是常用的数据结构,采用的FIFO(first in firstout)原则,新元素(等待进入队列的元素)总是被插入到尾部,而读取的时候总是从头部开始读取.在计算中队列一般用来做排队(如线程池的等待排队,锁的等待排队),用来做解耦(生产者消费者模式),异步等等.在java多线程应用中,队列的使用率很高,多数生产消费模型的首选数据结构就是队列.队列在现实生活中也很常见,例如去超市买东西排队付钱,先来的先付账走人回家,后来的要等前面的人付完钱才能付账. 首先我们看一段队列代码: type Queu

actomic cas无锁操作。

AtomicInteger中 维护了一个变量 volatile int value; AtomicLong中 维护了一个变量  volatile Long value; 这句unsafe.compareAndSwapInt(this, valueOffset, expect, update);等同这句 unsafe.compareAndSwapInt(value, expect, update);意思是 value等于expect 如果相同 则把value 值更新成update为什么是上面写法,

Java锁机制(二)

CAS无锁机制 CAS:Compare and Swap,即比较再交换. Java内存模型:JMM(Java Memory Model) 在内存模型当中定义了一个主内存,所有声明的实例变量都存在于主内存当中,主内存的数据会共享给所有线程,每一个线程有一块工作内存,工作内存当中主内存数据的副本 当更新数据时,会将工作内存中的数据同步到主内存当中 CAS无锁机制:本身无锁,采用乐观锁的思想,在数据操作时对比数据是否一致,如果一致代表之前没有线程操作该数据,那么就会更新数据,如果不一致代表有县城更新则

4.锁--无锁编程以及CAS

无锁编程以及CAS 无锁编程 / lock-free / 非堵塞同步 无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被堵塞的情况下实现变量的同步,所以也叫非堵塞同步(Non-blocking Synchronization). 实现非堵塞同步的方案称为"无锁编程算法"( Non-blocking algorithm). lock-free是眼下最常见的无锁编程的实现级别(一共三种级别). 为什么要 Non-blocking sync ? 使用lock实现线程同步