面试题答案
一键面试CAS原理
CAS是一种无锁算法,用于在多线程环境下实现变量的原子性更新。其基本原理是:当一个线程尝试更新一个共享变量时,它会将当前值与预期值进行比较。如果当前值与预期值相等,说明在这个线程检查变量值之后没有其他线程修改过该变量,那么就将变量更新为新的值;如果当前值与预期值不相等,说明有其他线程在这个线程检查变量值之后修改了该变量,那么这个线程就需要重新读取当前值,再次尝试更新操作。
基于CAS实现无锁队列入队操作
以下是使用Java的AtomicReference
来实现一个简单无锁队列的入队操作示例:
import java.util.concurrent.atomic.AtomicReference;
class Node<T> {
T value;
AtomicReference<Node<T>> next;
Node(T value) {
this.value = value;
this.next = new AtomicReference<>(null);
}
}
public class LockFreeQueue<T> {
private AtomicReference<Node<T>> head;
private AtomicReference<Node<T>> tail;
public LockFreeQueue() {
Node<T> dummy = new Node<>(null);
head = new AtomicReference<>(dummy);
tail = new AtomicReference<>(dummy);
}
public void enqueue(T item) {
Node<T> newNode = new Node<>(item);
while (true) {
Node<T> curTail = tail.get();
Node<T> next = curTail.next.get();
if (curTail == tail.get()) {
if (next == null) {
if (curTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(curTail, newNode);
return;
}
} else {
tail.compareAndSet(curTail, next);
}
}
}
}
}
可能存在的问题
- ABA问题:当一个变量从A变为B,再变回A时,CAS操作可能会误认为该变量没有被修改过。可以通过引入版本号(如
AtomicStampedReference
)来解决。 - 循环开销:由于CAS操作可能会失败,需要不断重试,这会带来额外的CPU开销,尤其是在竞争激烈的情况下。
- 饥饿问题:某些线程可能因为不断重试CAS操作而得不到执行机会,导致饥饿。可以通过公平调度算法来缓解。