Как различаются роль стека и кучи в многопоточном Java-приложении?

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

Ответ

В многопоточности различие критично для потокобезопасности и производительности.

Стек (Stack) — приватная память потока:

  • Каждый поток имеет свой собственный стек.
  • В стеке хранятся локальные переменные (примитивы и ссылки) и фреймы вызовов методов.
  • Доступ к стеку осуществляется только его потоком-владельцем, поэтому данные в стеке по умолчанию потокобезопасны.

Куча (Heap) — общая память:

  • Все потоки разделяют одну кучу.
  • В куче хранятся все объекты и их поля (статические и экземплярные).
  • Совместный доступ требует явной синхронизации (через synchronized, volatile, java.util.concurrent классы) для избежания состояний гонки (race condition), повреждения данных и проблем с видимостью.

Практический пример с проблемой:

public class SharedCounter {
    // Общее состояние в КУЧЕ - требует защиты
    private int count = 0;

    // НЕПОТОКОБЕЗОПАСНО - состояние гонки
    public void unsafeIncrement() {
        count++; // Операция "читать-изменить-записать" не атомарна
    }

    // ПОТОКОБЕЗОПАСНО через синхронизацию
    public synchronized void safeIncrement() {
        count++;
    }
}

public class Worker implements Runnable {
    private SharedCounter counter;
    private int localTasks; // Локальная переменная - в СТЕКЕ каждого потока, безопасна

    public void run() {
        for (int i = 0; i < 1000; i++) {
            localTasks++; // Безопасно, т.к. у каждого потока своя копия
            counter.safeIncrement(); // Безопасно, т.к. доступ синхронизирован
        }
    }
}

Итог:

  • Локальные переменные (в стеке) — безопасны для потоков.
  • Разделяемые объекты и их поля (в куче) — опасны, требуют стратегии синхронизации.
  • Статические поля также хранятся в куче (в области Metaspace/PermGen для Class-данных) и являются общими для всех потоков.