Combine CAS and volite to realize lock-free programming

CASE

That is, Compare and Swap, which embodies the idea of ​​optimistic locking. For example, multiple threads need to perform
a +1 operation on a shared integer variable :

https://pan.baidu.com/s/1IRDGlgIKP5CtE8EtY379kg

// 需要不断尝试
while(true) {
int 旧值 = 共享变量 ; // 比如拿到了当前值 0
int 结果 = 旧值 + 1; // 在旧值 0 的基础上增加 1 ,正确结果是 1
/*
这时候如果别的线程把共享变量改成了 5,本线程的正确结果 1 就作废了,这时候
compareAndSwap 返回 false,重新尝试,直到:
compareAndSwap 返回 true,表示我本线程做修改的同时,别的线程没有干扰
*/
if( compareAndSwap ( 旧值, 结果 )) {
// 成功,退出循环
}
}

When obtaining a shared variable, in order to ensure the visibility of the variable, volatile modification is required. Combining CAS and volatile can achieve lock-free concurrency, which is suitable for scenarios with low competition and multi-core CPUs.

  • Because synchronized is not used, the thread will not be blocked, which is one of the factors for efficiency improvement
  • But if the competition is fierce, you can imagine that retrying will happen frequently, but the efficiency will be affected.

The underlying CAS relies on an Unsafe class to directly call the CAS instruction at the bottom of the operating system. The following is an example of directly using the Unsafe object for thread safety protection

package cn.itcast.jvm.t5;

import sun.misc.Unsafe;

import java.lang.reflect.Field;

public class TestCAS {
    public static void main(String[] args) throws InterruptedException {
        DataContainer dc = new DataContainer();
        int count = 5;
        for (int i = 0; i < 1000; i++) {
            Thread t1 = new Thread(() -> dc.increase());
            t1.start();
        }
//        t1.join();
        Thread.sleep(2000);
        System.out.println(dc.getData());
    }
}

class DataContainer {
    private volatile int data = 0;
    static final Unsafe unsafe;
    static final long DATA_OFFSET;

    static {
        try {
// Unsafe 对象不能直接调用,只能通过反射获得
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            unsafe = (Unsafe) theUnsafe.get(null);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new Error(e);
        }
        try {
// data 属性在 DataContainer 对象中的偏移量,用于 Unsafe 直接访问该属性
            DATA_OFFSET =
                    unsafe.objectFieldOffset(DataContainer.class.getDeclaredField("data"));
        } catch (NoSuchFieldException e) {
            throw new Error(e);
        }
    }

    public void increase() {
        int oldValue;
        while (true) {
// 获取共享变量旧值,可以在这一行加入断点,修改 data 调试来加深理解
            oldValue = data;
// cas 尝试修改 data 为 旧值 + 1,如果期间旧值被别的线程改了,返回 false
            if (unsafe.compareAndSwapInt(this, DATA_OFFSET, oldValue, oldValue +
                    1)) {
                return;
            }
        }
    }

    public void increase2() {
        synchronized (TestCAS.class) {
            data++;
        }
    }

    public void decrease() {
        int oldValue;
        while (true) {
            oldValue = data;
            if (unsafe.compareAndSwapInt(this, DATA_OFFSET, oldValue, oldValue -
                    1)) {
                return;
            }
        }
    }

    public int getData() {
        return data;
    }
}

4.2 Optimistic and pessimistic locking

  • CAS is based on the idea of ​​optimistic locking: the most optimistic estimate, I am not afraid of other threads to modify the shared variable, even if it is changed, it does not matter, I will suffer a bit and try again.
  • Synchronized is based on the idea of ​​pessimistic locking: the most pessimistic estimate is that you have to prevent other threads from modifying shared variables. You don't want to change it after I am locked. You will have a chance after I have changed and learned about unlocking.

4.3 Atomic operation class

Atomic operation classes are provided in juc ​​(java.util.concurrent), which can provide thread-safe operations, such as: AtomicInteger, AtomicBoolean, etc. The bottom layer of them is implemented using CAS technology + volatile.
You can use AtomicInteger to rewrite the previous example:

// 创建原子整数对象
private static AtomicInteger i = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int j = 0; j < 5000; j++) {
i.getAndIncrement(); // 获取并且自增 i++
// i.incrementAndGet(); // 自增并且获取 ++i
}
});

 

Guess you like

Origin blog.csdn.net/nmjhehe/article/details/109451872