Atomic class and CAS principle in java

在java.util.concurrent.atomic包中提供了很多原子类,包括三个原子更新基本类型:AtomicBoolean,AtomicInteger,AtomicLong;原子更新数组:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray;原子更新引用类型:AtomicReferenceFieldUpdater,AtomicMarkableReference,AtomicReference;原子更新字段类:AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicStampedReference。

The update principle of atomic classes is the same. Here we no longer get a detailed introduction to the use of each type of atomic update, but use AtomicInteger to explain the use of the basic types of atomic update. The following methods are provided by AtomicInteger:

Method name

Method introduction

int addAndGet(int delta)

Atomically combine the entered value with the value in the instance (the AtomicInteger in

value) to add and return the result.

boolean compareAndSet(int expect,int update)

If the entered value is equal to the expected value,

The formula sets the value to the entered value.

int getAndIncrement ()

Add 1 to the current value atomically. Note that what is returned here is the value before the increment.

void lazySet(int newValue)

Eventually it will be set to newValue. After setting the value using lazySet, it may cause other

The thread can still read the old value for a short period of time

int getAndSet(int newValue)

Atomically set to the value of newValue and return the old value

The following code is an example of using AtomicInteger. The use of other atomic operation classes is basically similar. After using the example, we will introduce the basic principles of atomic operations.

public class AtomicIntegerDemo {
    static AtomicInteger ai = new AtomicInteger(1);
    public static void main(String[] args) {
        System.out.println(ai.getAndIncrement());
        System.out.println(ai.get());
    }
}

Below we introduce the update principle of AtomicInteger, how AtomicInteger enters the atomic update, first we look at the getAndIncrement method as follows:

public final int getAndIncrement() {
    //循环
    for (;;) {
        int current = get();
        int next = current + 1;
        //比较并且设置值
        if (compareAndSet(current, next))
        return current;
    }
}
public final boolean compareAndSet(int expect, int update) {
    //只有期待的值和更新的值一样才会更新
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

In the above code, we saw compareAndSwap again. In the concurrent package, this is a word with a very high usage rate, which is what we call CAS operation. In the CAS operation, the first step of the for loop body first obtains the value stored in AtomicInteger, the second step adds 1 to the current value of AtomicInteger, and the key third step calls the compareAndSet method to perform the atomic update operation. This method first check the current value is equal to current, equal to the mean value AtomicInteger not modified by another thread, the current value will be updated to the value of AtomicInteger next, if unequal compareAndSet method returns false, the program will enter
into circulation for re compareAndSet operating. This is the basic principle of CAS. Below we will introduce the use of CAS in detail and the use of CAS in other concurrent packages.

CAS (Compare And Swap) compares and exchanges. The CAS algorithm contains three parameters Cas(V, E, N); V represents the variable to be updated, E represents the expected value, and N represents the new value. When and only if the V value is equal to the E value, the V value will be set to N. If the V value is not equal to the E value, the operation will be returned without any operation. For example, in the above code, only when valueOffset is equal to expect, will valueOffset be set to update. In fact, CAS adopts the idea of ​​optimistic locking, and always thinks that it can complete the operation. Here, the spin operation is introduced, which is the for(;;) in the above code. If the setting fails, it will continue to spin until it is set. So far. The CAS operation in the JVM is implemented using the CMPXCHG instruction provided by the processor. The basic idea of ​​spin CAS is to loop CAS operations until it succeeds. The three CAS methods provided by java are as follows:

public final native boolean compareAndSwapObject(Object o,long offset,Object expected,Object x);
public final native boolean compareAndSwapInt(Object o, long offset,int expected,int x);
public final native boolean compareAndSwapLong(Object o, long offset,long expected, long x);

Although CAS adopts a lock-free approach to ensure atomic operations, it also creates a new problem that is the ABA problem. Because CAS needs to check whether the value has changed when operating the value, and update it if there is no change. Therefore, if there are two threads, the first thread fetches the value A from the memory, and the second thread fetches the value A. If it first changes to B and then changes to A, then it will be checked when using CAS It is found that its value has not changed, but it has actually changed. This is the ABA problem.

The solution to the ABA problem is to use the version number. Add the version number in front of the variable, and add 1 to the version number every time the variable is updated, then A→B→A will become 1A→2B→3A. Starting from Java 1.5, a class AtomicStampedReference is provided in the Atomic package of the JDK to solve the ABA problem. The function of the compareAndSet method of this class is to first check whether the current reference is equal to the expected reference, and check whether the current flag is equal to the expected flag, and if they are all equal, then atomically set the reference and the value of the flag to the given updated value.

Guess you like

Origin blog.csdn.net/wk19920726/article/details/108433671