Ответ
В многопоточности различие критично для потокобезопасности и производительности.
Стек (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-данных) и являются общими для всех потоков.