Вопрос проверяет понимание распределения ответственности между stack и heap при работе нескольких потоков.
Stack используется для хранения контекста выполнения каждого потока.
Heap используется для хранения объектов, доступных нескольким потокам.
Stack — приватный для потока, heap — общий.
Именно heap требует синхронизации при многопоточном доступе.
При выполнении потоков JVM использует stack и heap совместно, но с разными ролями.
Stack — это “как выполняется код”, heap — это “где живут объекты”.
В stack каждого потока JVM хранит:
Кадры вызова методов
Локальные переменные
Ссылки на объекты в heap
Адреса возврата
Stack:
Приватен для потока
Не требует синхронизации
Очищается автоматически
В heap:
Хранятся все объекты (new)
Объекты могут быть доступны нескольким потокам
Именно здесь возникают race condition
Пример:
class Counter {
int value;
}
Counter counter = new Counter();
Ссылка counter лежит в stack потока
Сам объект Counter — в heap
Если ссылку передать другому потоку, объект станет shared
Проблемы возникают, когда:
Несколько потоков читают и пишут один и тот же объект
Нет happens-before отношений
Изменения становятся невидимыми или перезаписываются
Поэтому используются:
synchronized
volatile
Классы из java.util.concurrent
Хорошая практика:
Делать объекты неизменяемыми
Минимизировать shared state
Максимум логики держать в локальных переменных
Stack и heap дополняют друг друга: stack изолирует выполнение, heap позволяет обмен данными.
Проблемы многопоточности всегда связаны с heap, а не со stack.