solitaryclown

Cas

2021-12-22
solitaryclown

CAS

解释

CAS,compare and set,是原子操作。

原理

在CAS底层原理是lock cmpxchg指令(x86架构)。 在多核CPU中,某个核心执行到带lock的指令时会把CPU总线锁住,指令执行完毕再开启总线,此过程不会被线程的调度打断,保证了操作的原子性。

例子

CAS是一种操作而不是具体类的,但java.util.concurrent.atomic提供了很多原子类来供我们使用这种操作,下面看一个使用AtomicInteger原子类实现线程安全的例子:

package com.huangbei.cas;


import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;

public class TestAccount {

    public static void main(String[] args) {
        Account accountUnsafe = new AccountUnsafe(10000);
        Account accountSafe = new AccountSafe(10000);

        Account.test(accountUnsafe);
        Account.test(accountSafe);

    }
}

class AccountSafe implements Account{
    private AtomicInteger balance;

    public AccountSafe(int balance) {
        this.balance=new AtomicInteger(balance);
    }

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

    @Override
    public void withDraw(int amount) {
        while (true){
            int prev=balance.get();
            int next = prev - amount;
            boolean success = balance.compareAndSet(prev, next);
            if(success){
                break;
            }
        }
    }
}
class AccountUnsafe implements Account {
    private Integer balance;

    public AccountUnsafe(int balance) {
        this.balance = balance;
    }

    @Override
    public int getBalance() {
        return this.balance;
    }

    @Override
    public void withDraw(int amount) {
        this.balance -= amount;
    }
}

interface Account {
    int getBalance();

    void withDraw(int amount);

    static void test(Account account) {
        ArrayList<Thread> threads = new ArrayList<>();
        //创建一千个线程,每个调用withDraw(10)

        long start = System.currentTimeMillis();

        int n = 1000;
        for (int i = 0; i < n; i++) {
            threads.add(new Thread(() -> {
                account.withDraw(10);
            }));

        }
        threads.forEach(Thread::start);
        threads.forEach(t -> {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        long end = System.currentTimeMillis();
        System.out.println("balance:" + account.getBalance());
        System.out.println("time:" + (end - start) + " ms");
        System.out.println("--------------------------");

    }
}

volatile在CAS中的应用

事实上,CAS操作需要volatile的支持AtomicInteger原子类中,它的value变量被定义为下面这样:

private volatile int value;

volatile是为了保证共享变量的可见性,保证一个线程对共享变量的写能被其他线程在下一次读时可见,所有线程每次读取到的共享变量均是最新值。

CAS和synchronized

CAS适用多核CPU、线程数较少的场景,避免因竞争锁失败阻塞导致的频繁切换线程上下文。 在线程竞争激烈的情况下,synchronized效率比CAS操作更高。


Comments

Content