Какие области памяти являются потокобезопасными
Что проверяют
Это вопрос с подвохом: ни одна область памяти не потокобезопасна сама по себе. Потокобезопасность это свойство доступа к данным, а не места, где они лежат. Интервьюер ждёт, что вы это скажете и разберёте, где данные изолированы, а где общие. Следом обычно просят найти гонку в коде ViewModel.
Как отвечать
- стек потока: локальные переменные и параметры видны только своему потоку. Но в стеке лежит лишь ссылка, сам объект в куче, и если ссылку отдали другому потоку, изоляции больше нет;
- куча: общая для всех потоков. Изменяемые объекты требуют синхронизации:
synchronized,ReentrantLock,AtomicIntegerиAtomicReference,ConcurrentHashMap; - ThreadLocal: сам объект в куче, но у каждого потока своё значение;
- неизменяемые объекты: безопасно читаются из любого потока.
final-поля по модели памяти Java видны корректно после конструктора без синхронизации, еслиthisне утёк из конструктора; - статическая инициализация: инициализация класса потокобезопасна, на этом держатся ленивый синглтон через holder-класс и
objectв Kotlin.
В Android сверху добавляется главный поток. View не потокобезопасны, трогать их можно только с main-потока, иначе CalledFromWrongThreadException. В Kotlin эту дисциплину дают корутины: работа уходит в Dispatchers.IO или Dispatchers.Default, результат возвращается через withContext(Dispatchers.Main) или StateFlow, а общее состояние меняют через MutableStateFlow.update {} или Mutex.
Где спотыкаются
Считают, что @Volatile делает операцию атомарной. Он даёт видимость изменений, но count++ остаётся тремя шагами, и инкременты теряются.
Говорят «локальные переменные всегда безопасны» и забывают про Kotlin: изменяемая var, захваченная лямбдой, заворачивается в объект-обёртку в куче и становится общей для всех потоков, которые эту лямбду выполняют.
Берут ConcurrentHashMap и делают «проверил и положил» двумя вызовами. Каждый вызов атомарен, пара нет, для этого есть computeIfAbsent и merge.