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操作更高。