多线程-无锁并发CAS,volatile,juc原子类

无锁并发就是并发情况下对共享资源不进行上锁,不上锁意味着有资源被线程获取后另一个线程竞争不会进入阻塞。无锁设计时就是不断whlie true循序直至获取到资源就退出。这种经典设计就是CAS。所以CAS也必须是搭配while(true)使用。volatile关键字是通过缓存一致性协议来保证变量的可见性和有序性。JUC原子类内的方法CAS实现的。下面具体分析下。

CAS

ReentrantLock接口中其实采用了CAS的思想,这个方法底层来源于AQS。再底层是基于unsafe类的native方法去实现无锁并发。CAS调用只需要传入旧值与新值,如果旧值不正确则不会修改成功。CAS如果只用CompareAndSet的话会有ABA问题。所谓ABA问题就是Thread1将A改为B,Thread2又将B改为A,Thread3进行CAS时会成功,但其实过程中也被其他线程改过。ABA问题不会对结果产生影响,只是过程不透明解决ABA问题就是加入版本号,或者改动标识位。类似实现JUC下有AtomicStampedReference,AtomicMarkableReference类。它们的CompareAndSet是需要多传入版本号,或者改动标识位的。测试类

public class TestCas {
    public static void main(String[] args) {
        Sell sell1 = new Sell(10000);
        for (int i =0;i<100;i++){
            new Thread(()->{
                sell1.sell(10);
            }).start();
        }
        SleepUtils.sleep(3);
        System.out.println(sell1.getBalance());
    }

}

class Sell {

    private AtomicInteger balance;
    
    public Sell(int value){
        this.balance = new AtomicInteger(value);
    }

    public int getBalance(){
        return balance.get();
    }

    public void sell(int amount){
        while (true){
            int balance1 = getBalance();
            int next = balance1 - amount;
            if (balance.compareAndSet(balance1,next)) {
                break;
            }
        }
    }
}

Volatile

volatile底层原理JMM是通过内存屏障去实现,有读屏障和写屏障。读屏障保证在读取共享变量所有的值都是主存最新的值,写屏障保证写入变量的值对所有工作内存可见。简单地说,被volatile修饰的变量当被改变时,会强制其它线程的工作内存中储存该值的引用失效,强制读取主内存的值。以下方法测试,如果不加volatile就不会结束循环。

@Slf4j
public class TestVolilite {
    // 易变
    volatile static boolean run = true;

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while(true){
                if(!run) {
                    break;
                }
            }
        });
        t.start();

        SleepUtils.sleep(1);
        run = false; // 线程t不会如预想的停下来
    }
}

JUC原子类 

JUC下面的类都是原子性的。但他们的底层实现都一致,比如AtomicInteger中,需要原子的修改一个整型值,底层都是unasfe的cas方法,但是该整型值是会被volatile修饰以保证其它线程可见性。

除整型外,还有bolean类型,long类型,原子数组,原子引用满足各种场景。具体方法可以看API。

猜你喜欢

转载自blog.csdn.net/weixin_42740540/article/details/124753452