Какие ресурсы общие между потоками
Что проверяют
Понимаете ли вы разницу между потоком и процессом и почему общая память требует синхронизации. Следом обычно просят объяснить, чем @Volatile отличается от synchronized, или спрашивают, что меняется с корутинами.
Как отвечать
Сначала разделите общее и собственное. Общее у всех потоков одного процесса:
- куча: все объекты, синглтоны,
companion object, статические поля,Application; - ресурсы процесса: открытые файлы и сокеты, соединения с базой, загруженные классы;
- главный поток как ресурс: интерфейс трогать можно только из него, иначе
CalledFromWrongThreadException.
Своё у каждого потока: стек с локальными переменными и кадрами вызовов, счётчик команд, значения ThreadLocal, а у потоков с очередью сообщений ещё и свой Looper.
Дальше про проблемы общей памяти. Гонка возникает на операциях «прочитать, изменить, записать»: count++ не атомарен. Видимость описывает Java Memory Model: без @Volatile или синхронизации один поток может не увидеть запись другого. Инструменты: synchronized и ReentrantLock для критических секций, AtomicInteger и ConcurrentHashMap для простых случаев, а в корутинах Mutex из kotlinx.coroutines.sync, который приостанавливает корутину, а не блокирует поток. MutableStateFlow безопасно обновлять через update { }.
Отдельно про Android. Каждое приложение обычно работает в своём процессе со своим экземпляром ART, так что потоки разных приложений память не делят. Но компонент с android:process в манифесте запускается в отдельном процессе, и синглтон там будет своим экземпляром.
Где спотыкаются
Говорят, что у каждого потока своя память, или путают поток с процессом. Своё у потока только стек и локальный контекст, куча общая.
Считают, что @Volatile делает count++ атомарным. Он даёт только видимость и порядок, для инкремента нужен AtomicInteger или блокировка.
Забывают, что корутина после приостановки может продолжиться на другом потоке диспетчера. Значение ThreadLocal при этом теряется, если не передать его через asContextElement().