多线程进阶:CAS机制

面试题:

  1. 讲解下你自己理解的 CAS 机制
  2. ABA问题怎么解决?

1、 什么是 CAS

CAS: 全称Compare and swap,字面意思:”比较并交换“,一个 CAS 涉及到以下操作:

我们假设内存中的原数据V,旧的预期值A,需要修改的新值B。 
 - 比较 A 与 V 是否相等。(比较) 
 - 如果比较相等,将 B 写入 V。(交换) 
 - 返回操作是否成功。

当多个线程同时对某个资源进行CAS操作,只能有一个线程操作成功,但是并不会阻塞其他线程,其他线程只会收到操作失败的信号。可见 CAS 其实是一个乐观锁

2 、CAS 是怎么实现的

针对不同的操作系统,JVM 用到了不同的 CAS 实现原理,简单来讲:

  • java 的 CAS 利用的的是 unsafe 这个类提供的 CAS 操作;
  • unsafe 的 CAS 依赖了的是 jvm 针对不同的操作系统实现的 Atomic::cmpxchg;
  • Atomic::cmpxchg 的实现使用了汇编的 CAS 操作,并使用 cpu 硬件提供的 lock 机制保证其原子性

简而言之,是因为硬件予以了支持,软件层面才能做到。

3、 CAS 有哪些应用

可以用于实现自旋锁,演示代码:

public class SpinLock {
 private AtomicReference<Thread> sign =new AtomicReference<>();
 public void lock(){
   Thread current = Thread.currentThread();
 
   // 不放弃 CPU,一直在这里旋转判断
   while(!sign .compareAndSet(null, current)){
  }
}
 public void unlock (){
   Thread current = Thread.currentThread();
   sign.compareAndSet(current, null);
}
}

用于实现原子类,示例代码:

public class AtomicInteger {
  public final int incrementAndGet() {
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
 }
}
public class Unsafe {
  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;
 }
}

无锁编程:不使用锁,直接使用CAS来保证线程安全。
下边我详细介绍一下某个变量利用CAS进行 ++ 操作,
来看看下边伪代码:

int size = 0;// 成员变量
// 多线程调用 add

void add() {
	int oldSize = size;
	while(!CAS(&size,oldSize,oldSize +1)) {
		oldSize = size;
	}
}

多线程进阶:CAS机制_第1张图片
对于线程1:

int oldSize = size;
while(!CAS(&size,oldSize,oldSize +1)) {
		oldSize = size;
	}

认为size 和 oldsize 相等就更新 size 值(说明没有被其他线程修改),并且返回true,循环结束。
对于线程2:

int oldSize = size;
while(!CAS(&size,oldSize,oldSize +1)) {
		oldSize = size;
	}

此时判断size 的值和oldSize不相等,不更新 size 值,并返回flase循环就要继续

int oldSize = size;
while(!CAS(&size,oldSize,oldSize +1)) {
		oldSize = size;
	}

此时 oldsize和size 相等此时修改size, 并且返回true,循环结束。

这个代码的本意就是,根据 oldSize 的值来判断读取 size 和修改size 之间是否被其他线程 改过了,但是也存在另外一种情况就是,在两个线程之间可能出现其他线程针对size 进行修改,,但是也有可能改了之后又被其他线程把size 给改回去。这也就是ABA问题。
标准库中也基于CAS封装了一些原子类:
多线程进阶:CAS机制_第2张图片

4、CAS的缺点

a)CPU开销较大

在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很大的压力。

b)不能保证代码块的原子性

CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用Synchronized了。

c)ABA问题

CAS的缺陷就是区分不了这两种情况:
该线程看到size = 0;
1、size就没有修改过就是原来的值;
2、size 被别的线程改成了1,又被另一个线程修改为0.
这就是ABA问题。
解决ABA问题思路:
解决ABA最简单的方案就是给值加一个修改版本号,每次值变化,都会修改它版本号,CAS操作时都对比此版本号。

你可能感兴趣的:(操作系统,java,多线程,并发编程)