Gymterview
middle

В чем заключаются различия между стеком (stack) и кучей (heap) с точки зрения многопоточности?

В контексте многопоточности стек и куча играют принципиально разные роли.

Стек (Stack)

Стек – приватная память потока. Каждый поток имеет собственный стек вызовов, который хранит:

  • Локальные переменные методов
  • Параметры методов
  • Адреса возврата при вызове методов
  • Промежуточные результаты вычислений

Данные в стеке недоступны другим потокам – это обеспечивает автоматическую потокобезопасность локальных переменных.

Куча (Heap)

Куча – общая память всех потоков. В ней хранятся:

  • Все объекты (созданные через new)
  • Массивы
  • Статические поля классов

Данные в куче доступны всем потокам, что создаёт необходимость синхронизации доступа.

Сравнение

Характеристика Стек Куча
Принадлежность Один поток Все потоки
Видимость данных Только владелец Любой поток
Потокобезопасность Автоматическая Требует синхронизации
Размер Ограничен (-Xss, по умолчанию ~512 КБ-1 МБ) Общий, большой (-Xmx)
Создание/удаление Автоматически при входе/выходе из метода Сборщик мусора (GC)
Хранимые данные Примитивы и ссылки Объекты

Кэширование и volatile

Для повышения производительности каждый поток может кэшировать значения переменных из кучи в свой рабочий кэш (CPU cache). Это означает, что изменение переменной одним потоком может быть невидимо другим потокам. Для решения этой проблемы используется ключевое слово volatile, которое запрещает кэширование и гарантирует, что значение всегда читается из основной памяти.

Пример
Поток A                  Основная память (куча)         Поток B
┌──────────┐             ┌─────────────────┐           ┌──────────┐
│ Кэш: x=5 │  ← read    │      x = 10     │   read →  │ Кэш: x=5│
│           │            │                 │           │          │
│ Стек:     │            │ (volatile x=10  │           │ Стек:    │
│  local=42 │            │  видно всем)    │           │  local=7 │
└──────────┘             └─────────────────┘           └──────────┘

Важный нюанс: ссылка в стеке, объект в куче

Локальная переменная, содержащая ссылку на объект, хранится в стеке, но сам объект – в куче. Если два потока имеют ссылки на один объект, они оба работают с объектом в куче, и нужна синхронизация:

Пример
public void method() {
    // list — ссылка в стеке (потокобезопасна)
    // объект ArrayList — в куче (общий, если передан другому потоку)
    List<String> list = sharedList;
    list.add("item"); // Небезопасно без синхронизации!
}

Аналогия из жизни. Стек – это ваш личный блокнот: только вы его видите и пишете в нём. Куча – это общая доска в офисе: все сотрудники могут читать и писать, но если двое одновременно пишут в одном месте, информация перемешается. volatile – это правило «перед тем как писать, стереть свой черновик и прочитать доску заново».

На собеседовании. Ключевые моменты: (1) стек приватен для потока, куча общая; (2) локальные примитивы в стеке потокобезопасны автоматически; (3) объекты всегда в куче, даже если ссылка на них – локальная переменная; (4) из-за кэширования изменения в куче могут быть невидимы другим потокам без volatile или synchronized.