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-цикла:
- Поток читает текущее состояние.
- Подготавливает новое состояние.
- Атомарно пытается заменить старое на новое (
compareAndSet). - Если другой поток успел изменить состояние — CAS вернёт
false, и цикл повторится.
Такой подход гарантирует прогресс: хотя бы один поток всегда продвигается вперёд (lock-free гарантия).
На собеседовании могут спросить о проблеме ABA: если между чтением и CAS значение изменилось с A на B и обратно на A, CAS не заметит изменения. Для стека это обычно не критично, но в общем случае решается через
AtomicStampedReference.