手写无锁栈和队列:CAS的极致应用🔓

53 阅读2分钟

无锁数据结构,性能爆表!但要小心ABA问题和内存泄漏。

一、无锁栈(Treiber Stack)

实现

public class LockFreeStack<T> {
    
    private static class Node<T> {
        final T value;
        Node<T> next;
        
        Node(T value) {
            this.value = value;
        }
    }
    
    private final 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));  // CAS
    }
    
    // 出栈
    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));  // CAS
        
        return oldTop.value;
    }
}

ABA问题解决

public class SafeLockFreeStack<T> {
    
    private static class Node<T> {
        final T value;
        Node<T> next;
        
        Node(T value) {
            this.value = value;
        }
    }
    
    // 使用版本号
    private final AtomicStampedReference<Node<T>> top = 
        new AtomicStampedReference<>(null, 0);
    
    public void push(T value) {
        Node<T> newNode = new Node<>(value);
        int[] stampHolder = new int[1];
        Node<T> oldTop;
        
        do {
            oldTop = top.get(stampHolder);
            int stamp = stampHolder[0];
            newNode.next = oldTop;
        } while (!top.compareAndSet(oldTop, newNode, stamp, stamp + 1));
    }
    
    public T pop() {
        int[] stampHolder = new int[1];
        Node<T> oldTop;
        Node<T> newTop;
        
        do {
            oldTop = top.get(stampHolder);
            if (oldTop == null) {
                return null;
            }
            int stamp = stampHolder[0];
            newTop = oldTop.next;
        } while (!top.compareAndSet(oldTop, newTop, stamp, stamp + 1));
        
        return oldTop.value;
    }
}

二、无锁队列(Michael-Scott Queue)

实现

public class LockFreeQueue<T> {
    
    private static class Node<T> {
        final T value;
        final AtomicReference<Node<T>> next = new AtomicReference<>();
        
        Node(T value) {
            this.value = value;
        }
    }
    
    private final AtomicReference<Node<T>> head;
    private final AtomicReference<Node<T>> tail;
    
    public LockFreeQueue() {
        Node<T> dummy = new Node<>(null);
        head = new AtomicReference<>(dummy);
        tail = new AtomicReference<>(dummy);
    }
    
    // 入队
    public void enqueue(T value) {
        Node<T> newNode = new Node<>(value);
        
        while (true) {
            Node<T> curTail = tail.get();
            Node<T> tailNext = curTail.next.get();
            
            if (curTail == tail.get()) {
                if (tailNext != null) {
                    // 尾指针落后,先推进
                    tail.compareAndSet(curTail, tailNext);
                } else {
                    // 尝试插入
                    if (curTail.next.compareAndSet(null, newNode)) {
                        // 成功,推进尾指针
                        tail.compareAndSet(curTail, newNode);
                        return;
                    }
                }
            }
        }
    }
    
    // 出队
    public T dequeue() {
        while (true) {
            Node<T> curHead = head.get();
            Node<T> curTail = tail.get();
            Node<T> headNext = curHead.next.get();
            
            if (curHead == head.get()) {
                if (curHead == curTail) {
                    if (headNext == null) {
                        return null;  // 队列空
                    }
                    // 尾指针落后,推进
                    tail.compareAndSet(curTail, headNext);
                } else {
                    T value = headNext.value;
                    if (head.compareAndSet(curHead, headNext)) {
                        return value;
                    }
                }
            }
        }
    }
}

三、性能对比

// 测试代码(略)

结果:

实现吞吐量(ops/秒)竞争程度
synchronized栈500万
无锁栈2000万
BlockingQueue300万
无锁队列1500万

四、注意事项⚠️

  1. ABA问题:用AtomicStampedReference
  2. 内存泄漏:及时清理节点
  3. 性能:竞争激烈时性能下降
  4. 复杂性:实现复杂,容易出错

下一篇→ Executor框架的设计哲学🏗️