Appearance
CAS操作面试题
1. CAS的基本概念
问题:什么是CAS?
答案:
- CAS(Compare-And-Swap):比较并交换,是一种无锁的并发算法。
- 作用:
- 实现无锁并发
- 避免线程阻塞
- 提高并发性能
- 原理:
- 读取当前值
- 比较当前值和期望值
- 如果相等,更新为新值
- 如果不等,重试
2. CAS的实现原理
问题:CAS是如何实现的?
答案:
- 硬件支持:
- CAS依赖CPU的原子指令
- x86架构:CMPXCHG指令
- ARM架构:LDREX/STREX指令
- Java实现:
- Unsafe类提供CAS操作
- Atomic类基于Unsafe实现
示例:
java
// CAS的伪代码实现
public boolean compareAndSwap(int[] arr, int index, int expected, int newValue) {
if (arr[index] == expected) {
arr[index] = newValue;
return true;
}
return false;
}3. CAS的Java实现
问题:Java中如何使用CAS?
答案:
java
// 使用Unsafe类
public class CASExample {
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset(CASExample.class.getDeclaredField("value"));
} catch (Exception e) {
throw new Error(e);
}
}
private volatile int value;
public boolean compareAndSwap(int expected, int newValue) {
return unsafe.compareAndSwapInt(this, valueOffset, expected, newValue);
}
}
// 使用Atomic类
AtomicInteger atomicInteger = new AtomicInteger(0);
boolean success = atomicInteger.compareAndSet(0, 1);4. Atomic类
问题:Java提供了哪些Atomic类?
答案:
- 基本类型:
- AtomicInteger
- AtomicLong
- AtomicBoolean
- 数组类型:
- AtomicIntegerArray
- AtomicLongArray
- AtomicReferenceArray
- 引用类型:
- AtomicReference
- AtomicStampedReference
- AtomicMarkableReference
- 字段更新器:
- AtomicIntegerFieldUpdater
- AtomicLongFieldUpdater
- AtomicReferenceFieldUpdater
- 累加器:
- LongAdder
- DoubleAdder
- LongAccumulator
- DoubleAccumulator
5. AtomicInteger的使用
问题:如何使用AtomicInteger?
答案:
java
AtomicInteger atomicInteger = new AtomicInteger(0);
// 获取值
int value = atomicInteger.get();
// 设置值
atomicInteger.set(10);
// CAS操作
boolean success = atomicInteger.compareAndSet(0, 1);
// 原子加
int result = atomicInteger.getAndIncrement();
int result = atomicInteger.incrementAndGet();
// 原子减
int result = atomicInteger.getAndDecrement();
int result = atomicInteger.decrementAndGet();
// 原子加指定值
int result = atomicInteger.getAndAdd(5);
int result = atomicInteger.addAndGet(5);
// 原子更新
int result = atomicInteger.getAndUpdate(x -> x * 2);
int result = atomicInteger.updateAndGet(x -> x * 2);6. AtomicReference的使用
问题:如何使用AtomicReference?
答案:
java
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
AtomicReference<User> userRef = new AtomicReference<>(new User("John", 30));
// 获取值
User user = userRef.get();
// CAS操作
User newUser = new User("Jane", 25);
boolean success = userRef.compareAndSet(user, newUser);
// 原子更新
User result = userRef.getAndUpdate(u -> new User(u.name, u.age + 1));7. AtomicIntegerArray的使用
问题:如何使用AtomicIntegerArray?
答案:
java
int[] array = new int[10];
AtomicIntegerArray atomicArray = new AtomicIntegerArray(array);
// 获取值
int value = atomicArray.get(0);
// 设置值
atomicArray.set(0, 10);
// CAS操作
boolean success = atomicArray.compareAndSet(0, 10, 20);
// 原子加
int result = atomicArray.getAndIncrement(0);
int result = atomicArray.incrementAndGet(0);8. AtomicStampedReference的使用
问题:什么是ABA问题?如何解决?
答案:
- ABA问题:
- 一个值从A变成B,又变回A
- CAS无法检测到这种变化
- 解决方案:
- 使用版本号
- 使用AtomicStampedReference
示例:
java
AtomicStampedReference<String> stampedRef = new AtomicStampedReference<>("A", 0);
// 获取值和版本号
String value = stampedRef.getReference();
int stamp = stampedRef.getStamp();
// CAS操作
boolean success = stampedRef.compareAndSet("A", "B", 0, 1);9. LongAdder的使用
问题:什么是LongAdder?何时使用?
答案:
- LongAdder:高并发场景下的累加器,性能优于AtomicLong。
- 适用场景:
- 高并发累加
- 统计计数
- 原理:
- 分散竞争
- 减少CAS失败
示例:
java
LongAdder adder = new LongAdder();
// 累加
adder.add(1);
adder.add(2);
// 获取值
long sum = adder.sum();
// 重置
adder.reset();10. CAS的优缺点
问题:CAS有哪些优缺点?
答案:
- 优点:
- 无锁,避免线程阻塞
- 性能高
- 适合低竞争场景
- 缺点:
- 高竞争时性能下降
- 只能保证单个变量的原子性
- ABA问题
- CPU开销大
11. CAS的ABA问题
问题:什么是ABA问题?
答案:
- ABA问题:
- 一个值从A变成B,又变回A
- CAS无法检测到这种变化
- 可能导致数据不一致
- 解决方法:
- 使用版本号
- 使用AtomicStampedReference
- 使用AtomicMarkableReference
示例:
java
// ABA问题示例
AtomicInteger atomicInteger = new AtomicInteger(100);
// 线程1:100 -> 101 -> 100
atomicInteger.compareAndSet(100, 101);
atomicInteger.compareAndSet(101, 100);
// 线程2:CAS成功,但值已经被修改过
boolean success = atomicInteger.compareAndSet(100, 200);12. CAS的自旋锁
问题:如何使用CAS实现自旋锁?
答案:
java
class SpinLock {
private final AtomicReference<Thread> owner = new AtomicReference<>();
public void lock() {
Thread currentThread = Thread.currentThread();
while (!owner.compareAndSet(null, currentThread)) {
// 自旋等待
}
}
public void unlock() {
Thread currentThread = Thread.currentThread();
owner.compareAndSet(currentThread, null);
}
}13. CAS的性能问题
问题:CAS在什么情况下性能会下降?
答案:
- 高竞争:
- 多个线程同时修改同一个变量
- CAS失败率高
- 自旋时间长
- 解决方案:
- 减少竞争
- 使用分段锁
- 使用LongAdder
14. CAS的内存语义
问题:CAS的内存语义是什么?
答案:
- volatile语义:
- CAS操作具有volatile语义
- 保证可见性
- 保证有序性
- happens-before原则:
- CAS操作之前的操作对CAS操作可见
- CAS操作之后的操作对CAS操作可见
15. CAS与synchronized的区别
问题:CAS和synchronized有什么区别?
答案:
- CAS:
- 无锁
- 非阻塞
- 适合低竞争
- 只能保证单个变量的原子性
- synchronized:
- 有锁
- 阻塞
- 适合高竞争
- 可以保证代码块的原子性
16. CAS的使用场景
问题:CAS适合哪些场景?
答案:
- 计数器:
- 并发计数
- 使用AtomicInteger
- 序列号生成:
- 原子递增
- 使用AtomicLong
- 状态标志:
- 状态切换
- 使用AtomicBoolean
- 引用更新:
- 原子更新引用
- 使用AtomicReference
17. CAS的局限性
问题:CAS有哪些局限性?
答案:
- ABA问题:
- 无法检测值的变化
- 只能保证单个变量的原子性:
- 无法保证多个变量的原子性
- 高竞争性能差:
- CAS失败率高
- 自旋时间长
- CPU开销大:
- 自旋占用CPU
18. CAS的优化
问题:如何优化CAS性能?
答案:
- 减少竞争:
- 使用分段锁
- 使用ThreadLocal
- 使用LongAdder:
- 高并发累加
- 分散竞争
- 限制自旋次数:
- 避免无限自旋
- 自旋一定次数后阻塞
19. CAS在Java中的应用
问题:CAS在Java中有哪些应用?
答案:
- 并发包:
- AtomicInteger
- AtomicLong
- AtomicReference
- 集合类:
- ConcurrentHashMap
- ConcurrentLinkedQueue
- 锁实现:
- ReentrantLock
- StampedLock
20. CAS的最佳实践
问题:使用CAS的最佳实践有哪些?
答案:
- 选择合适的Atomic类。
- 避免ABA问题,使用AtomicStampedReference。
- 高并发累加使用LongAdder。
- 高竞争场景使用synchronized。
- 限制自旋次数,避免CPU浪费。
- 理解CAS的内存语义。
- 合理使用CAS的原子方法。
- 注意CAS的局限性。
- 根据场景选择合适的并发工具。
