CAS和volite结合使用,实现无锁编程

CAS

即 Compare and Swap ,它体现的一种乐观锁的思想,比如多个线程要对一个共享的整型变量执
行 +1 操作:

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

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

获取共享变量时,为了保证该变量的可见性,需要使用 volatile 修饰。结合 CAS 和 volatile 可以实现无锁并发,适用于竞争不激烈、多核 CPU 的场景下。

  • 因为没有使用 synchronized,所以线程不会陷入阻塞,这是效率提升的因素之一
  • 但如果竞争激烈,可以想到重试必然频繁发生,反而效率会受影响

CAS 底层依赖于一个 Unsafe 类来直接调用操作系统底层的 CAS 指令,下面是直接使用 Unsafe 对象进行线程安全保护的一个例子

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 乐观锁与悲观锁

  • CAS 是基于乐观锁的思想:最乐观的估计,不怕别的线程来修改共享变量,就算改了也没关系,我吃亏点再重试呗。
  • synchronized 是基于悲观锁的思想:最悲观的估计,得防着其它线程来修改共享变量,我上了锁你们都别想改,我改完了解开锁,你们才有机会。

4.3 原子操作类

juc(java.util.concurrent)中提供了原子操作类,可以提供线程安全的操作,例如:AtomicInteger、AtomicBoolean等,它们底层就是采用 CAS 技术 + volatile 来实现的。
可以使用 AtomicInteger 改写之前的例子:

// 创建原子整数对象
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
}
});

 

猜你喜欢

转载自blog.csdn.net/nmjhehe/article/details/109451872