【面试:并发篇32:cas】原子类型
00.前言
如果有任何问题请指出,感谢。
01.解释
JUC提供了一些实现了cas的工具类:三大类 原子整数 原子引用 原子数组
02.原子整数
有AtomicBoolean、AtomicInteger、AtomicLong,这里我们拿AtomicInteger举例
AtomicInteger i = new AtomicInteger(5);
System.out.println(i.incrementAndGet()); // ++i
System.out.println(i.getAndIncrement()); // i++
System.out.println(i.decrementAndGet()); // --i
System.out.println(i.getAndDecrement()); // i--
System.out.println(i.getAndAdd(5)); // 先打印再自增5
System.out.println(i.addAndGet(5)); // 先自增5再打印
i.updateAndGet(value -> value * 10);
// 内部是函数式接口 可以使用lambda表达式
// 可以自定义运算方式,更新但没有返回值
i.getAndUpdate(value -> value * 10);
// 返回更新后的返回值
上述都是AtomicInteger类常用的方法 方法对应的操作都是原子的 不会出现线程安全问题 注释中有对应的操作。
03.原子引用
补充
在学下面内容之前,我们要明确一个概念,cas操作中的比较的是地址值或者地址的内容?答案是地址值,下面的ABA问题会体现。
为什么要有原子引用类型
我们知道类型分为基本类型(int long char等)和引用类型(类对象),上述我们的原子整数对应的就是基本类型的cas实现,现在的原子引用就是引用类型的cas实现。
AtomicReference< Object>
介绍
泛型里放要使用的引用类型 保证它的原子性
例子
还是上一篇文章的取钱例子,只不过把int类型换为了BigDecimal类型,我们现在是否还会出现线程安全问题
代码
@Slf4j(topic = "c.Test35")
public class Test35 {
public static void main(String[] args) {
DecimalAccount.demo(new DecimalAccountCas(new BigDecimal("10000")));
}
}
class DecimalAccountCas implements DecimalAccount {
private AtomicReference<BigDecimal> balance;
public DecimalAccountCas(BigDecimal balance) {
// this.balance = balance;
this.balance = new AtomicReference<>(balance);
}
@Override
public BigDecimal getBalance() {
return balance.get();
}
@Override
public void withdraw(BigDecimal amount) {
while(true) {
BigDecimal prev = balance.get();
BigDecimal next = prev.subtract(amount);
if (balance.compareAndSet(prev, next)) {
break;
}
}
}
}
interface DecimalAccount {
// 获取余额
BigDecimal getBalance();
// 取款
void withdraw(BigDecimal amount);
/**
* 方法内会启动 1000 个线程,每个线程做 -10 元 的操作
* 如果初始余额为 10000 那么正确的结果应当是 0
*/
static void demo(DecimalAccount account) {
List<Thread> ts = new ArrayList<>();