[JAVAEE] 面试题(二) - CAS 和 原子类
目录
一. CAS的实现原理
1.1 伪代码分析
1.2 底层实现
二. CAS 操作示例
三. ABA问题
四. 原子类
4.1 使用原子类的目的
4.2 原子类的使用示例
五. 总结
一. CAS的实现原理
CAS(compare and swap 比较和交换)是一种用于实现无锁并发的技术.
1.1 伪代码分析
// 伪代码 boolean CAS(address, expectValue, swapValue) { if (&address == expectValue) { &address = swapValue; return true; } return false; }
&address: 需要修改的值.
expectValue: 预期的值.
swapValue: 新值, 需要赋值给&address的值.
核心思想: 比较当前需要修改的值和预期的值是否相同, 如果相同, 就用新值替换需要修改的值.
1.2 底层实现
CAS无锁并发技术底层是靠C语言依赖的操作系统的原子操作来实现原子性的. 即在这个过程中不会被其他线程打断(CAS可以理解为是一个赋值操作, 这是原子性的, 不会产生线程安全问题).
二. CAS 操作示例
假设有一个整数变量count, 初始值为0, 现在有A, B两个线程同时对count进行+1操作.(使用CAS无锁并发技术)
1. 线程A读取变量count的值, 得到0.
2. 线程B读取变量count的值, 得到0.
3. 线程A将变量count的值与预期的值0进行比较, 相等, 将count的值增加为1.
4. 线程B将变量count的值与预期的值0进行比较, 不相等(count的值已经被线程A增加为1), 则重试.
5. 线程B重新读取变量count的值, 得到1.
6. 线程B将变量count的值与预期的值1进行比较, 相等, 将count的值增加为2.
最终, count的值为2.
三. ABA问题
假设有两个线程, 线程1使用CAS技术将val的值从A修改成B, 又把val的值从B修改成A. 线程2通过CAS技术判断线程1没有对val的值进行修改, 于是线程2再次修改val的值(实际上不需要修改). 这就是 ABA 问题.
解决:
在CAS操作的时候对val加版本号.
A 1.0
B 2.0
A 3.0
java中的 AtomicStampedReference类 解决了ABA问题.
四. 原子类
java中的原子类在 java.util.concurrent.atomic 包中.
4.1 使用原子类的目的
为了不加锁, 提高程序运行效率.
4.2 原子类的使用示例
实现一个无锁的计数器.
static AtomicInteger count = new AtomicInteger();
public static void main(String[] args) throws InterruptedException{
Thread t1 = new Thread(() -> {
for (int i = 0; i < 50000; i++) {
count.getAndIncrement();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 50000; i++) {
count.getAndIncrement();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count.get());
}
五. 总结
1. CAS(compare and swap) 是一种无锁并发技术.
2. CAS的伪代码分析: 将需要修改的值和预期的值进行比较, 如果相等, 就用新值替换需要修改的值.
3. CAS的底层实现: CAS底层是靠C语言依赖的操作系统的原子操作来实现的原子性,(本质上是赋值操作, 是原子性的, 是线程安全的.)
4. ABA问题, 解决(对CAS操作需要修改的值加版本号).
5. 使用原子类的目的.(为了避免加锁操作, 提高程序运行效率).