CAS算法原理分析(java中原子类如何实现线程安全)

1. 目标:

从原码层面分析CAS算法、以及java.util.concurrent.atomic 包下的原子类是如何运用CAS算法而实现线程安全。

2. 基础知识

CAS算法基本原理

CAS算法全称:compare and swap (比较并交换),是CPU指令级的操作,只有一步原子操作,整个操作是原子的,也就是要么不执行,要么执行完。这样的系统原子操作能做什么呢?比较内存中的参数值和方法调用处提供的参数值,如果相等,则将内存的参数值,设置为新值,否则返回内存中的参数值。

volatile 关键字

这个关键字修饰的变量,能保证变量在线程之间可见。再解释下,如果2个线程同时操作一个被volatile关键字修饰的变量,各个线程都会将变量从内存统一拷贝到自己的所在cpu的缓存中,当一个线程对变量进行修改之后,该变量位于其他线程的cpu缓存中拷贝能立即感知到。并同时调整为一致。

Unsafe 类

大家都知道java一般很少直接操作内存,但是其实还是有一个类是可以直接操作修改内存的,他就是Unsafe。要获取一个对象的某个字段的值是多少,我们应该知道几个重要参数,第一个是对象在内存中的起始位置,第二个是需要获取的字段在内存中的相对地址,第三个是字段的长度,也就是需要取几个字节,才对应这个字段的值。

3. 从源码角度分析AtomicInteger是如何实现线程安全的

AtomicInteger的成员变量介绍

 private static final Unsafe unsafe = Unsafe.getUnsafe();//用于操作内存的Unsafe实例
 private static final long valueOffset;//AtomicInteger所保存的int值在内存中的偏移量
 private volatile int value; //AtomicInteger中保存的int值,被volatile关键字修饰,确保线程之间可见。
 static {
        try {
            //实际设置了int值在内存中的偏移
            valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

下面是我们征对常用的方法进行介绍

//在当前值的基础上自动+1,并且返回新值
 public final int incrementAndGet() {
      return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}

这里我们看底层是调用了unsafe的getAndAddInt方法,我们跟进去

//obj 是AtomicInteger对象
//index long型的内存偏移量
//addVale 本次操作需要增加的值
public final int getAndAddInt(Object obj, long index, int addVale) {
        int oldValue;
        do {
            oldValue = this.getIntVolatile(obj, index);//先从内存里取的参数的值
        } while(!this.compareAndSwapInt(obj, index, oldValue, oldValue + addVale));

        return oldValue;//返回老值
    }
//这个方法被final标记了,说明是一个本地方法,我们从openjdk里面把相应的c语言代码拿出来,主要要记住,下面的compareAndSwapInt方法是一个原子操作。
public final native boolean compareAndSwapInt(Object obj, long index, int oldValue, int newValue);
int compare_and_swap (int* reg, int oldval, int newval) 
{
  ATOMIC();
  int old_reg_val = *reg;
  if (old_reg_val == oldval) 
     *reg = newval;
  END_ATOMIC();
  return old_reg_val;
}

简单分析上面的代码,线程1和线程2并行修改值,一开始大家拿到的值都是55,线程1希望+1,改为56,线程2也希望+1,但是如果在线程1先+1的情况下,线程再在55的基础上+1,就没有意义了,必须要在56的基础上+1。我们逐行分析下代码。
在这里插入图片描述
在这里插入图片描述

CAS算法缺点

CAS虽然很高效的解决了原子操作问题,但是CAS仍然存在三大问题。

  1. 循环时间长开销很大。
  2. 只能保证一个共享变量的原子操作。
  3. ABA问题

猜你喜欢

转载自blog.csdn.net/ygy982883422/article/details/106607719