MST

星途 面试题库

面试题:在Java中如何通过CAS操作实现一个无锁的线程安全栈,并分析可能遇到的ABA问题及解决思路

要求用Java代码实现一个基于CAS操作的无锁线程安全栈。在实现过程中,思考可能会遇到ABA问题的地方,并阐述你将如何检测和解决ABA问题,说明不同解决方法的优缺点。
45.4万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

基于CAS操作的无锁线程安全栈实现

import java.util.concurrent.atomic.AtomicReference;

class Node<T> {
    T value;
    Node<T> next;

    Node(T value) {
        this.value = value;
    }
}

public class LockFreeStack<T> {
    private AtomicReference<Node<T>> top = new AtomicReference<>();

    public void push(T value) {
        Node<T> newNode = new Node<>(value);
        Node<T> oldTop;
        do {
            oldTop = top.get();
            newNode.next = oldTop;
        } while (!top.compareAndSet(oldTop, newNode));
    }

    public T pop() {
        Node<T> oldTop;
        Node<T> newTop;
        do {
            oldTop = top.get();
            if (oldTop == null) {
                return null;
            }
            newTop = oldTop.next;
        } while (!top.compareAndSet(oldTop, newTop));
        return oldTop.value;
    }
}

ABA问题及解决方法

1. 版本号(Version Number)

  • 检测方法:为每个节点添加一个版本号字段,每次节点状态变化时版本号递增。在进行CAS操作时,不仅比较节点的引用,还比较版本号。
  • 解决方法:在节点类中添加一个AtomicInteger类型的版本号字段。例如:
class Node<T> {
    T value;
    Node<T> next;
    AtomicInteger version = new AtomicInteger(0);

    Node(T value) {
        this.value = value;
    }
}
  • 优点:简单直接,能够有效检测和解决ABA问题。
  • 缺点:增加了额外的空间开销,每次操作都需要更新版本号,增加了一定的时间开销。

2. 时间戳(Timestamp)

  • 检测方法:与版本号类似,为每个节点记录一个时间戳,每次节点状态变化时更新时间戳。CAS操作时比较时间戳。
  • 解决方法:在节点类中添加一个long类型的时间戳字段,并在每次节点状态变化时更新时间戳。例如:
class Node<T> {
    T value;
    Node<T> next;
    long timestamp = System.currentTimeMillis();

    Node(T value) {
        this.value = value;
    }
}
  • 优点:能有效检测ABA问题,且时间戳具有唯一性,在分布式系统中也适用。
  • 缺点:同样增加了空间开销,获取时间戳的操作可能有一定性能损耗,而且时间戳可能存在时钟回退等问题。

3. 原子标记(Atomic Mark)

  • 检测方法:为节点添加一个标记字段,用于标记节点是否被删除或修改。在CAS操作时检查标记。
  • 解决方法:在节点类中添加一个AtomicBoolean类型的标记字段,当节点被删除或修改时设置标记。例如:
class Node<T> {
    T value;
    Node<T> next;
    AtomicBoolean isMarked = new AtomicBoolean(false);

    Node(T value) {
        this.value = value;
    }
}
  • 优点:空间开销相对较小,能检测到节点的关键状态变化。
  • 缺点:只能检测节点是否被标记,无法精确跟踪节点变化的次数,可能存在误判情况。