CAS implementation principle

foreword

JUC is the abbreviation of java.util.concurrent package, JUC has two cores, CAS and AQS, CAS is the basis of java.util.concurrent.atomic package

@NotThreadSafe
public class CountTest {

    public static int count = 0;

    public static void main(String[] args) {

        //新建一个线程池
        ExecutorService service = Executors.newCachedThreadPool();
        //Java8 lambda表达式执行runnable接口
        for (int i = 0; i < 5; i++) {
            service.execute(() -> {
                for (int j = 0; j < 1000; j++) {
                    count++;
                }
            });
        }
        //关闭线程池
        service.shutdown();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("count = " + count);
    }
}

Since this code is thread-unsafe, the final result may be less than 500. We can use synchronized to ensure the atomicity and visibility of the operation

@ThreadSafe
public class CountTest {

    public static int count = 0;

    public static void main(String[] args) {

        ExecutorService service = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            service.execute(() -> {
                for (int j = 0; j < 1000; j++) {
                    synchronized (CountTest.class) {
                        count++;
                    }
                }
            });
        }
        service.shutdown();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("count = " + count);
    }
}

Synchronized is a pessimistic lock. It has an obvious disadvantage. It locks regardless of whether there is competition for data storage. As the amount of concurrency increases, and if the lock time is relatively long, its performance overhead will become very large. Is there a way to fix this? The answer is optimistic locking based on conflict detection. In this mode, there is no so-called lock concept. Each thread directly performs the operation first to detect whether there is a shared data competition with other threads. If there is no shared data competition, let the operation succeed. Perform the operation until it succeeds, the process of retrying is called spinning

The java.util.concurrent.atomic package uses CAS. For example, AtomicInteger can be used for atomic operations of Integer type. The above code can be changed to the following, which is also thread-safe

@ThreadSafe
public class CountTest {

    public static AtomicInteger count = new AtomicInteger(0);

    public static void main(String[] args) {

        ExecutorService service = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            service.execute(() -> {
                for (int j = 0; j < 1000; j++) {
                    count.getAndIncrement();
                }
            });
        }
        service.shutdown();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("count = " + count);
    }
}

Introduction to CAS

CAS (Compare and Swap), translated into Compare and Swap.

CAS has 3 operands, the memory value V, the old expected value A, and the new value B to be modified. Modify memory value V to B if and only if expected value A and memory value V are the same, otherwise do nothing.

write picture description here

Source code analysis

Based on jdk1.8.0_20

private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;

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

private volatile int value;

The value of AtomicInteger is stored in value, the visibility of the operation is guaranteed by volatile, and it is guaranteed by a static code block that valueOffset already has a value when the class is loaded

Unsafe is an unsafe class that provides some underlying operations. We cannot use this class. valueOffset is the offset of the value member variable of the AtomicInteger object in memory

public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}
//第一个参数为当前这个对象,如count.getAndIncrement(),则这个参数则为count这个对象
//第二个参数为AtomicInteger对象value成员变量在内存中的偏移量
//第三个参数为要增加的值
public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        //调用底层方法得到value值
        var5 = this.getIntVolatile(var1, var2);
        //通过var1和var2得到底层值,var5为当前值,如果底层值=当前值,则将值设为var5+var4,并返回true,否则返回false
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

    return var5;
}

This method is implemented by other languages, so it is no longer analyzed

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

It is more suitable to use CAS when the concurrency is relatively low, and it is more suitable to use synchronized when the concurrency is relatively high
. Disadvantages of CAS

  1. Only atomic operations on a variable are guaranteed
  2. Spinning for a long time can stress the CPU
  3. ABA problem

Reference blog

[1]https://mp.weixin.qq.com/s?__biz=MzIxMjE5MTE1Nw==&mid=2653192625&idx=1&sn=cbabbd806e4874e8793332724ca9d454&chksm=8c99f36bbbee7a7d169581dedbe09658d0b0edb62d2cbc9ba4c40f706cb678c7d8c768afb666&scene=21#wechat_redirect
[2]https://mp.weixin.qq.com/s/nRnQKhiSUrDKu3mz3vItWg
[3]https://blog.csdn.net/pi9nc/article/details/39177343
[4]https://blog.csdn.net/tanga842428/article/details/52742698

Guess you like

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