JDK CAS and fully understand the contract JUC in cas use volatile variable optimistic spin lock mode to solve the multi-thread synchronization issues with CAS univariate shortcomings ABA resource consumption

Disclaimer: This article is a blogger original article, shall not be reproduced without the bloggers allowed. https://blog.csdn.net/c5113620/article/details/90046572

When it comes to multi-thread synchronization cas solve the problem, see a person has no real understanding cas, cas is not heard him say before and after using the cpu machine instructions to complete the setup value ensures atomicity, so basic is Baidu out of one hundred, there is no good understand the real process of cas

[Points] cas

  1. cas method of synchronization variables [ must ] be volatile type
  2. Spin cas (infinite loop), before and after the value change is determined to ensure that the value of the multi-thread synchronization

JUC is jdk and contract [ directory referred ]

In the internal source AtomicInteger example, jdk1.8.0_202

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

Some also show the source code is this

public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return next;
    }
}

public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

When loaded into AtomicInteger jvm bytecode class files, static blocks perform static initialization, to obtain an internal valuevariable offset length

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

value is volatile type, value is the value represented by AtomicInteger

private volatile int value;

Then mainly to see how to solve the multi-thread variables cas cycle synchronization problem is how to solve this problem i ++, so as to truly understand cas lock-free synchronization mechanism

循环的过程主要是两步,可以理解为两个原子操作

1. 获取当前值【最新】值,volatile属性
int current = get(); 
或者
//var1是实例对象this,var2是valueOffset就是value的偏移长度,这样也可以得到value最新值
var5 = this.getIntVolatile(var1, var2); 

2. 判断与修改值
 含义:通过对象this与变量偏移地址valueOffset取得变量值
 //如果与预期expect (就是上面的原子操作取得的值) 一致,说明这两步原子操作中间【没有】被【并发修改】,可以设置为update
 //与预期expect不一致,进入下次循环。
 //说明value被其他线程修改了,为了保证变量同步,应该重新获取一次最新值
 //也就是当前线程的这两个原子操作中间被中断,线程挂起,被其他线程执行了cas设置
 //通过对象this与变量偏移地址valueOffset可以获取最新被修改后的值 (volatile属性)(这是在cas内部处理)
 //expect就是第一个原子操作取得的current值, 用于校验这两个原子操作是否被中断,从而保证【多线程单变量同步】
 
unsafe.compareAndSwapInt(this, valueOffset, expect, update) 
或者
this.compareAndSwapInt(var1, var2, var5, var5 + var4)

From the above description of the source, and I can be seen that a spin process is two atomic cas

  1. When there is no concurrency, directly cas more successful, then set successfully
  2. When concurrent, concurrent threads variable is modified, it will cause the internal cas valueOffset acquired by the offset address value of the first latest acquired values ​​are inconsistent atomic operation, and judges that the variable has been modified by other threads, multi-threaded to ensure the shared variables synchronization, enter the next cycle, acquire new value, continue to conduct a comparative until the other thread has not been modified, the setting is successful. Here you can see how much thread contention, only one thread can cas success, other threads in the constant cycle retry cas

CAS issue

  1. We can only guarantee a volatile variable lock-free synchronization
即使使用多个cas设置多个volatile变量,但是不能保证这些多个volatile变量被同时在一起修改的原子性
没有原子性,就会出现并发问题,变量值不正确。
  1. ABA problem
虽然CAS保证了两个原子操作的前后的【值】一样,但是不能保证这个值没有被重新设置为【一样】的值
比如这个值可能是某个链表的指针,虽然地址是一样,执行同一个地方,但是你不知道,在第一个原子操作后,线程挂起
其他线程修改了这个链表后,恰巧把相同的地址放在这里,等你第二个原子操作开始,虽然【值】是一样的
但是这个【新值】所指的含义,或者这个【新值】本身的含义不同于原来的【旧值】
  1. Performance issues
从我上面的分析,看出,虽然cas无锁同步,保证了多线程下的单变量同步
但当需要cas同一变量的线程太多,因为同一时刻,只有一个线程可以cas成功
余下的线程都在循环cas,线程越多,争用越大,循环时间就长,消耗cpu时间

Guess you like

Origin blog.csdn.net/c5113620/article/details/90046572