Atomic Variables of JUC and CAS Algorithm

Talk is cheap, show u the code:

public class TestAtomicDemo {

    public static void main(String[] args) {
        AtomicDemo ad = new AtomicDemo();
        for (int i = 0; i < 10; i++) {
            new Thread(ad).start();
        }
    }
}

class AtomicDemo implements Runnable {

    private int seriaNumber = 0;

    @Override
    public void run() {

        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + ":" + getSeriaNumber());
    }

    public int getSeriaNumber() {
        return seriaNumber++;
    }
}

What is the output? Why?

Enter image description

Note: It does not necessarily produce the same results as above, nor does it necessarily cause thread safety issues. Thread safety issues occur randomly and may require multiple runs.

At this point, a thread safety problem has occurred, and ten threads cannot evenly add serialNumber to 9

Visibility issues? Then try adding volatile

public class TestAtomicDemo {

    public static void main(String[] args) {
        AtomicDemo ad = new AtomicDemo();
        for (int i = 0; i < 10; i++) {
            new Thread(ad).start();
        }
    }
}

class AtomicDemo implements Runnable {

    private volatile int seriaNumber = 0;

    @Override
    public void run() {

        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + ":" + getSeriaNumber());
    }

    public int getSeriaNumber() {
        return seriaNumber++;
    }
}

Enter image description

Note: It does not necessarily produce the same results as above, nor does it necessarily cause thread safety issues. Thread safety issues occur randomly and may require multiple runs.

Analysis of the cause of the problem:

The atomicity of i++

For example:

int i = 10;
i = i++; 
System.out.print("i = " + i); // i = 10

The i = i++ operation is actually divided into three steps in memory

int temp = i;
i = i + 1;
i = temp;

When thread 1 executes i = i + 1, it updates the value of i to 0. Maybe thread 2 has read the value of temp to 0 at this time. Maybe after executing i = i + 1, the value of i is updated to 0

Solution:

Using Atomic Variables

public class TestAtomicDemo {

    public static void main(String[] args) {
        AtomicDemo ad = new AtomicDemo();
        for (int i = 0; i < 10; i++) {
            new Thread(ad).start();
        }
    }
}

class AtomicDemo implements Runnable {

//    private volatile int seriaNumber = 0;
    private AtomicInteger seriaNumber = new AtomicInteger();

    @Override
    public void run() {

        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + ":" + getSeriaNumber());
    }

    public int getSeriaNumber() {
//        return seriaNumber++;
        return seriaNumber.getAndIncrement();
    }
} 

Multiple runs found that the result will no longer have thread safety issues

Atomic variables: provided by JDK1.5

  1. Using volatile to ensure memory visibility
  2. The CAS (Compare And Swap) algorithm guarantees the atomicity of variables

The CAS algorithm is hardware support for shared variables of concurrent operations, and contains three operands:

  • memory value v
  • Estimated value A
  • update value B

Update the value of V to B if and only if V == A, otherwise do nothing

The atomic classes under JUC are all implemented through CAS. The following will take AtomicInteger as an example to illustrate the implementation of CAS. as follows:

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

Unsafe是CAS的核心类,Java无法直接访问底层操作系统,而是通过本地(native)方法来访问。不过尽管如此,JVM还是开了一个后门:Unsafe,它提供了硬件级别的原子操作。

valueOffset为变量值在内存中的偏移地址,unsafe就是通过偏移地址来得到数据的原值的。

value当前值,使用volatile修饰,保证多线程环境下看见的是同一个。

我们就以AtomicInteger的addAndGet()方法来做说明,先看源代码:


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

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


内部调用unsafe的getAndAddInt方法,在getAndAddInt方法中主要是看compareAndSwapInt方法:



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



吐血,写不下去了,写这篇学习笔记的时候还是有很多疑惑,先结掉,日后再修改。天呐,路漫漫其修远兮。。

Guess you like

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