ABA 问题示例

假设我们有一个简单的栈实现,用 CAS(Compare and Swap)来实现无锁出栈操作:

import java.util.concurrent.atomic.AtomicReference;

class Node {
    int value;
    Node next;

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

class Stack {
    AtomicReference<Node> head = new AtomicReference<>();

    void push(int value) {
        Node newNode = new Node(value);
        Node oldHead;
        do {
            oldHead = head.get();
            newNode.next = oldHead;
        } while (!head.compareAndSet(oldHead, newNode));
    }

    int pop() {
        Node oldHead;
        Node newHead;
        do {
            oldHead = head.get();
            if (oldHead == null) {
                throw new RuntimeException("Stack is empty");
            }
            newHead = oldHead.next;
        } while (!head.compareAndSet(oldHead, newHead));
        return oldHead.value;
    }
}

public class ABADemo {
    public static void main(String[] args) {
        Stack stack = new Stack();
        stack.push(1);
        stack.push(2);

        // Simulate ABA problem
        new Thread(() -> {
            int value = stack.pop(); // This should pop 2
            System.out.println("Popped: " + value);

            stack.push(2); // Push 2 back, creating ABA scenario
        }).start();

        new Thread(() -> {
            stack.pop(); // Incorrect assumptions due to ABA
        }).start();
    }
}

解决 ABA 问题

可以通过增加版本号来解决 ABA 问题:

import java.util.concurrent.atomic.AtomicStampedReference;

class StackWithVersion {
    AtomicStampedReference<Node> head = new AtomicStampedReference<>(null, 0);

    void push(int value) {
        Node newNode = new Node(value);
        int[] stampHolder = new int[1];
        Node oldHead;
        do {
            oldHead = head.get(stampHolder);
            newNode.next = oldHead;
        } while (!head.compareAndSet(oldHead, newNode, stampHolder[0], stampHolder[0] + 1));
    }

    int pop() {
        int[] stampHolder = new int[1];
        Node oldHead;
        Node newHead;
        do {
            oldHead = head.get(stampHolder);
            if (oldHead == null) {
                throw new RuntimeException("Stack is empty");
            }
            newHead = oldHead.next;
        } while (!head.compareAndSet(oldHead, newHead, stampHolder[0], stampHolder[0] + 1));
        return oldHead.value;
    }
}

public class ABAPreventionDemo {
    public static void main(String[] args) {
        StackWithVersion stack = new StackWithVersion();
        stack.push(1);
        stack.push(2);

        // Simulate ABA prevention
        new Thread(() -> {
            int value = stack.pop(); // This should pop 2
            System.out.println("Popped: " + value);

            stack.push(2); // Push 2 back
        }).start();

        new Thread(() -> {
            stack.pop(); // Correctly handled due to versioning
        }).start();
    }
}

在这个解决方案中,我们使用 AtomicStampedReference 来同时管理节点和版本号,确保即使值相同,版本号的变化也能被检测到。