CAS 工作方式-无锁并发安全

130 阅读3分钟

 我们在cas也就是Compare-And-Swap,简写为cas,我们先不去想什么是cas啊,我们先来想

我们在多线程情况下要修改一个变量的话,在不加锁的情况线程一旦开始就要一直运行到结束,这时候变量就会出现线程安全的问题,比较典型的是票的超卖问题,但是如果我们加锁的话,因为涉及到线程的上下文切换,阻塞等就不可避免的会出现效率变低的问题。

而cas 就根据乐观锁的思想,对一个变量的修改,来进行 不加锁保证效率的情况下,来对该变量进行 安全的并发修改

并发代码

我们来看下面的代码

这是一个接口

interface Account {
    Integer getBalance();

    void withDraw(Integer downBalance);
}

然后这是接口的实现类

class CasAccount implements Account {
    private AtomicInteger balance;

    public CasAccount(Integer balance) {
        this.balance = new AtomicInteger(balance);
    }

    @Override
    public Integer getBalance() {
        return balance.get();
    }

    @Override
    public void withDraw(Integer downBalance) {
        while (true) {
            int cur = balance.get();
            int nextBalance = cur - downBalance;
            if (balance.compareAndSet(cur, nextBalance)) {
                break;
            }
        }
    }
}

接下来是我们的多线程并发修改 balance 的值

public static void main(String[] args) {
        CasAccount casAccount = new CasAccount(1000);
        ArrayList<Thread> list = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            list.add(new Thread(new Runnable() {
                @Override
                public void run() {
                    casAccount.withDraw(10);
                }
            }));
        }
        list.forEach(Thread::start);
        list.forEach(t-> {
            try {
                t.join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        System.out.println(casAccount.getBalance());
    }

我们下面着重来看下面一段代码的逻辑

class CasAccount implements Account {
    private AtomicInteger balance;

    public CasAccount(Integer balance) {
        this.balance = new AtomicInteger(balance);
    }

    @Override
    public void withDraw(Integer downBalance) {
        while (true) {
            int cur = balance.get();
            int nextBalance = cur - downBalance;
            if (balance.compareAndSet(cur, nextBalance)) {
                break;
            }
        }
    }
}

在该实现类中,我们用的balance 是 原子类

AtomicInteger  

修饰的,他的底层就是用硬件来保证原子性,而不是加锁。

我们分析这段代码

  1. 线程1 进入循环,这时候获得balance的值 为1000,这时候cur设置为1000,然后 我们扣减余款10,这时候 nextBalance 被设置为990,然后 调用该 compareandset方法的时候,如果cur的值还等于原来的balance的值1000,我们会把该 类中私有的 AtomicInteger 类型的 balance 值 修改为 nextBanlce 990,这时候跳出循环,该线程结束 ,当然这是理想的情况

第一次分析 当然是 正常线程的正常修改的情况下,我们接下来分析其他线程修改的情况,当线程1进入循环,获得cur值为1000,然后nextbalance值为990,这时候来了线程二抢到cpu执行权,直接完成了修改操作,把balance值改为了990,这时候线程1继续运行,在atomicinterger.compareandset方法 比较私有化的balance值跟cur的1000比对,发现不对,这时候会继续返回false,然后重新进行循环.

这就是cas的实现

cas配合volatile

,然而这种实现 体现的是无锁并发,无阻塞式并发,当我们一个资源竞争的非常激烈的时候,这种一直循环带来的资源消耗也是比较大的。所以cas适用于线程数少,多核cpu的情况。而cas是怎么让一个资源在多个线程下可见的,他也是使用了volatile 让资源在线程中可见

​编辑