Gymterview
senior

Напишите минимальный неблокирующий стек (всего два метода — push() и pop())

Неблокирующий (lock-free) стек реализуется с помощью AtomicReference и алгоритма compare-and-set (CAS). Ни один поток не блокирует другой — при конкуренции поток просто повторяет попытку.

Код: lock-free стек на AtomicReference
class NonBlockingStack<T> {
    private final AtomicReference<Element> head = new AtomicReference<>(null);

    NonBlockingStack<T> push(final T value) {
        final Element current = new Element();
        current.value = value;
        Element recent;
        do {
            recent = head.get();           // Читаем текущую вершину
            current.previous = recent;      // Связываем новый элемент с текущей вершиной
        } while (!head.compareAndSet(recent, current)); // CAS: если вершина не изменилась — заменяем
        return this;
    }

    T pop() {
        Element result;
        Element previous;
        do {
            result = head.get();            // Читаем текущую вершину
            if (result == null) {
                return null;                // Стек пуст
            }
            previous = result.previous;     // Запоминаем предыдущий элемент
        } while (!head.compareAndSet(result, previous)); // CAS: если вершина не изменилась — удаляем
        return result.value;
    }

    private class Element {
        private T value;
        private Element previous;
    }
}

Принцип работы CAS-цикла:

  1. Поток читает текущее состояние.
  2. Подготавливает новое состояние.
  3. Атомарно пытается заменить старое на новое (compareAndSet).
  4. Если другой поток успел изменить состояние — CAS вернёт false, и цикл повторится.

Такой подход гарантирует прогресс: хотя бы один поток всегда продвигается вперёд (lock-free гарантия).

На собеседовании могут спросить о проблеме ABA: если между чтением и CAS значение изменилось с A на B и обратно на A, CAS не заметит изменения. Для стека это обычно не критично, но в общем случае решается через AtomicStampedReference.