В каком случае структура данных «куча» (Heap) не является потокобезопасной в Java?

«В каком случае структура данных «куча» (Heap) не является потокобезопасной в Java?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Куча (например, PriorityQueue) по умолчанию не потокобезопасна. Проблемы возникают при параллельной модификации из нескольких потоков без внешней синхронизации, что приводит к состоянию гонки (race condition), повреждению внутренней структуры данных и неопределённому поведению.

Пример небезопасного использования:

PriorityQueue<Integer> heap = new PriorityQueue<>();

// Поток 1
new Thread(() -> heap.add(1)).start();
// Поток 2
new Thread(() -> heap.add(2)).start();
// Результат: возможны исключения ConcurrentModificationException,
// потеря данных или некорректная структура кучи.

Как обеспечить потокобезопасность:

  1. Использовать блокирующую версию из java.util.concurrent:
    import java.util.concurrent.PriorityBlockingQueue;
    PriorityBlockingQueue<Integer> safeHeap = new PriorityBlockingQueue<>();
    // Теперь операции add, poll, peek потокобезопасны.
  2. Внешняя синхронизация с помощью Collections.synchronizedCollection():
    Queue<Integer> syncHeap = Collections.synchronizedCollection(new PriorityQueue<>());
    // Теперь необходим доступ через synchronized-блоки.
  3. Явная синхронизация (наименее предпочтительно):
    synchronized(heap) {
        heap.add(value);
    }

Ключевой вывод: Любая стандартная реализация кучи (PriorityQueue) небезопасна для многопоточного доступа "из коробки".