Concurrent programming (5): CAS

    In the atomic package, most classes are implemented with the help of unsafe classes, such as the following code

    public static AtomicInteger count = new AtomicInteger(0);

    private  static void add() {
        count.incrementAndGet();
    }

  The implementation of the incrementAndGet() method is as follows:

    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

Let's go deeper into the getAndInt() method, which is implemented as follows:

    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;
    }

  In the above code, what we are talking about is the compareAndSwapInt(var1, var2, var5, var5 + var4) method, compareAndSwap, which takes the initial letter of each word, which is what we often call cas. There are four parameters in this method, var1 is the current object, that is, the count in the code, and var2 is the current value. If you want to calculate the operation that 2 plus 1 equals 3, var2 is 2, and var4 is the increment, that is, in this example 1, var5 is the current value of the bottom layer obtained by calling the bottom layer method. If no other thread changes the current value of the bottom layer, the return value is 2. The function of the compareAndSwapInt method is that if var2 (current value) and var5 (the current value of the bottom layer are equal), then The underlying value is overwritten as the underlying current value (var5) + increment (var4), otherwise (other threads have changed the underlying current value, var5 is not equal to var2), re-take the value of var5 from the underlying method, so that var5=4, and Re-take the value of var2 from var1 (the current object) again, so that the value of var2 also becomes 4, and the addition operation is performed to cover the underlying value. If var2 and var5 are still not equal, continue to cycle until they are equal. 

 

  The implementation of the getIntVolatile method and compareAndSwapInt is as follows:

public native int getIntVolatile(Object var1, long var2);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

 Both methods can be modified by native, and the method modified by native is the bottom method, which is generally implemented by C language.

 

  Locks are divided into pessimistic locks and optimistic locks. An exclusive lock is a pessimistic lock, and synchronized is an exclusive lock. If the lock is occupied, other threads that need the lock will be suspended until the thread holding the lock releases the lock. The problems that will arise are as follows:

  a. Under multi-thread competition, locking and releasing locks will cause more context switching and scheduling delays, causing performance problems

  b. If a high-priority thread waits for a low-priority thread to release the lock, it will cause priority inversion, causing performance risks

 

  The other is optimistic locking. It does not lock each time it executes, but assumes that there is no conflict to complete the operation. If it fails due to conflict, it will retry until it succeeds. The mechanism used by objective locking is CAS. Although CAS can solve atomic operations efficiently, there are still three major problems with CAS:

  a, ABA problem, if a value was originally A, was changed to B, and then changed to A, then when using CAS to check, it was found that its value did not change, but it actually changed, the ABA problem The solution is to use the version number, add the version number in front of the variable, and increase the version by one each time the variable is updated. In the blog above, we mentioned that there are special classes in the atomic package to solve the ABA problem.

  b. The cycle time is long and the overhead is high. If the CAS loop is unsuccessful for a long time, it will bring a very large execution overhead to the CPU. If the JVM can support the pause instruction provided by the processor, the efficiency will be improved to a certain extent. The pause instruction has two functions. It delays the execution of the pipeline, so that the CPU does not consume too much execution resources and avoids the memory sequence conflict when exiting the loop. Causes the CPU pipeline to be emptied, improving CPU execution efficiency

  c. Only an atomic operation of a shared variable is guaranteed. When performing operations on a shared variable, we can use cyclic CAS to ensure atomic operations, but when operating on multiple shared variables, cyclic CAS cannot guarantee the atomicity of the operation. At this time, we can use locks or combine multiple shared variables. To operate as a shared variable or put multiple variables in an object (the atomic package has atomic operations on reference types) to perform CAS operations

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325382213&siteId=291194637