CAS是什么?

发布时间 2023-11-28 22:53:21作者: love~boy

CAS是什么

CAS是Compare-And-Swap(比较并交换)的缩写,是一种轻量级的同步机制,主要用于实现多线程环境下的无锁算法和数据结构,保证了并发安全性,它可以在不适用锁的情况下,对共享数据进行线程安全的操作。(ConcurentHashMap在JDK1.8开始使用的是CAS)

CAS操作主要有三个参数:要更新的内存位置、期望的值和新值。CAS操作的执行过程如下:

  • 1、首先,获取要更新的内存位置,记为var
  • 2、然后,将期望值expected与var进行比较,如果两者相等,则将内存位置的值var更新为新值new
  • 3、如果两者不相等,则说明有其他线程修改了内存位置的值var,此时CAS操作失败,需要重新尝试。

原理相关的Unsafe类

Unsafe类是JDK提供的不安全的类,提供了一些底层操作,包括内存操作、线程调度、对象实例化等。他的作用是让Java可以在底层直接操作内存,从而提高程序的效率。但是,Unsafe类是不安全的,所以只有JDK开发员才使用他,普通开发不建议使用。

由于CAS操作时基于底层硬件支持的原子性指令来实现的,所以它可以保证操作的原子性和线程安全性,同时也可以避免使用锁带来的性能开销。CAS主要用于并发编程中,比如实现无锁数据结构、实现线程安全的计数器等。

什么时ABA

ABA问题就是在CAS操作过程中,如果变量的值被从A改为B又改为A,而CAS操作是能够成功的,这就可能导致程序出现意外的结果。

解决方案

Java的AtomicStampedReference类,该类通过使用版本号的方式来解决ABA问题,每个共享变量都会关联一个版本号,CAS操作时需要同时检查值和版本号是否匹配。因此,如果共享变量被改变了,版本号就会发生变化,即使共享变量变为原来的值,版本号不同,CAS操作会失败。

CPU空转

为什么出现CPU空转

如果某个线程一直在自旋等待,会浪费CPU资源。

解决

当一个线程请求获取锁时,如果已经持有锁,那就计数器加1,否则使用CAS操作获取锁,避免使用synchronized关键字或ReentrantLock等锁的实现机制。

当线程获取失败,自旋等待,避免立刻进入阻塞状态,避免线程上下文的开销,当重试小于10时,使用自旋方式,当重试次数大于10时,就阻塞等待,这样可以在多线程环境下保证线程的公平性和效率。

释放锁时,如果计数器大于0,就减一,释放一次减一次,否则将锁的拥有者设置为null,唤醒其他线程。这样确保在多个线程持有锁的情况下,正确释放锁资源,并唤醒其他等待线程,保证线程正确性和公平性。

应用场景

在并发编程中广泛应用,通常实现乐观锁和无锁算法:
1、线程安全计数器
2、队列:并发编程中,队列经常用于线程之间的数据交换。使用CAS可以实现无锁的非阻塞队列。
3、数据库并发控制
4、自旋锁
5、线程池:多线程编程中,线程池可以提高线程的使用效率,使用CAS操作可以避免对线程池的加锁,从而提高线程池的并发性能。

CAS真的完全没加锁吗?

CAS是一个无锁机制的操作,底层是通过Unsafe类使用native本地方法进行CAS操作,但是硬件层面CAS是如何保证原子性?真的完全没加锁吗?
底层来看,CAS操作通常使用cmpxchg指令实现的。
这个指令如何保证原子性?

  • 1、cmpxchg指令是一个原子指令,在CPU执行cmpxchg指令时,处理器会自动锁定总线,防止其他CPU访问共享变量,然后执行比较和交换操作,最后释放总线。
  • 2、cmpxchg指令在执行期间,CPU会自动禁止中断。这样确保CAS原子性,避免中断和其他干扰对操作的影响。
  • 3、cmpxchg指令是硬件实现的,可以保证原子性和正确性。CPU中的硬件电路确保了cmpxchg指令的正确执行,以及对共享变量的访问是原子的。

所以操作系统层面,CAS会加锁,通过加锁的方式锁定总线,避免其他CPU访问共享变量。