Java atomic operation - principle analysis of Atomic package

Atomic:

    The Atomic package is another java package specially designed for thread safety under java.util.concurrent, which contains multiple classes of atomic operations. The basic feature is that in the case of multi-threading, when multiple threads want to operate some instance methods of these classes at the same time, they are exclusive, that is, when a thread is executing a method, it will not be interrupted by other threads. Other threads will wait outside until the method is executed, and then the JVM will select another thread from the waiting queue to enter, which is only a logical understanding. In fact, it is implemented with the help of hardware-related instructions, which will not block the thread (just block it at the hardware level). You can operate on basic data, basic data in arrays, and basic data in classes. The atomic variable class is equivalent to a generalized volatile variable that supports atomic and conditional read and write operations.

    Let's first look at how traditional locks ensure thread safety

    

class LockDemo {

    private int a;

    public synchronized void setA(int b) {
        this.a = b;
    }

    public synchronized int getA() {
        return a;
    }

    public synchronized void addA() {
        ++a;
    }

    public synchronized void reduceA() {
        --a;
    }

}

In fact, such synchronized can already meet our daily thread safety requirements. Synchronized is a mechanism based on code blocking, that is, when a thread occupies resources, other threads cannot enter. If there is a problem with this thread, a large number of If the thread is blocked, the CPU will spend a lot of resources to process these blocked threads, but the task of the CPU should not be like this, and there may be problems such as deadlock. For such a simple operation, it seems a bit cumbersome, so there should be more appropriate A more efficient way to deal with such a problem. So there is CAS

Compare and swap(CAS)

The current processors basically support CAS, which is a hardware-based processing. Each CAS operation contains three operators: the memory address V, an expected value A and a new value B. If the value is equal to the expected value A, then assign it to B, if not, do nothing. In short, your value is equal to my expected value, give you the new value, otherwise don't work.

We can simply simulate the implementation of CAS ourselves

class CASDemo {

    private int value;

    public synchronized int getValue() {
        return value;
    }

    public synchronized int cas(int expectValue , int newValue) {

        int oldValue = value;
        if(value == expectValue) {
            value = newValue;
        }
        return oldValue;

    }

}

class CASTest {

    private CASDemo casDemo;

    public int getValue () {
        return casDemo.getValue();
    }

    public int add () {
        int oldValue = casDemo.getValue();
        while(casDemo.cas(oldValue , oldValue + 1) != oldValue) {
            oldValue = casDemo.getValue();
        }

        return oldValue + 1;
    }

}

Check out the Atomic bag


Take a look at how the AtomicInteger class handles auto-increment processing, that is, the method getAndIncrement()


A method called the Unsafe class is called here. This class is more special and most of the internal methods are native, and this class does not allow us to use it at will. Of course, the JDK itself can be used. (ps: In the attitude of always trying, I I tried the method of calling it, and the error is as follows )

Take a look at where valueOffset comes from

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

这里又调用了Unsafe类的一个方法,参数是通过反射获取的AtomicInteger的value属性,也就是它的值

继续进

public native long objectFieldOffset(Field var1);


很可惜,是一个本地方法,查看文档可知,这个方法返回的就是"原来的值"的内存地址 , valueOffset的值(ps:文档是其他某位大神那里借来的)

返回一开始的方法 点进去看

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

参数比较混乱 var2就是valueOffset 继续跟进

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

喔,又是一个本地方法,参数很乱  按我们之前的讲就是:

 var2(valueOffset)  , var4(expectValue) , var5(newValue) 

我再借一点注释


这个方法原子的将变量的值更新为var5,如果成功了返回true。这样返回去取反即为false,循环结束,返回var5,取到新值。

当然Unsafe关于CAS的方法都是本地方法,是由C语言实现的,我们这里是看不到具体实现细节的。


    说了半天 到底CAS是怎么保证线程安全的呢,其实在语言层次我们是没有做任何关于同步的操作的,也没有任何锁。Atomic包下这些类将这些操作都交给了CPU和内存,利用CPU多处理的能力,实现硬件的阻塞,再加上volatile变量的特性即可实现基于原子性的操作的线程安全。所以CAS不是没有阻塞 ,只是阻塞不是语言层面,而是在硬件层面,这样便会更高效。



    

Guess you like

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