Что произойдет при одновременной записи в массив с разных потоков
Что проверяют
Понимаете ли вы, что гонка это не только «два потока перезаписали друг друга», но и проблема видимости памяти, и знаете ли, чем в Android закрывают общее изменяемое состояние.
Как отвечать
Сначала уточнить, о чём речь: голый массив фиксированной длины или ArrayList. У массива структура не рвётся, запись в разные индексы безопасна, но без happens-before другой поток может сколь угодно долго видеть старое значение: модель памяти Java этого не запрещает. Запись long и double без volatile вдобавок не атомарна и может порваться на две половины.
У ArrayList всё хуже: поле size и внутренний буфер меняются не атомарно, поэтому параллельный add теряет элементы, оставляет null в хвосте, роняет ArrayIndexOutOfBoundsException при расширении, а чтение одновременно с записью даёт ConcurrentModificationException, причём эта проверка на совесть, а не гарантия.
Чем чинят:
synchronized,ReentrantLockилиCollections.synchronizedListна весь доступ, и чтение тоже;AtomicIntegerArrayиAtomicReferenceArray, если нужны атомарные операции по индексу;CopyOnWriteArrayList, когда чтений намного больше записей: каждая запись копирует массив целиком;- в Kotlin:
Mutex.withLockвнутри корутины (не блокирует поток) либо владение состоянием одним диспетчером, а наружу неизменяемый снимок черезStateFlow.
Где спотыкаются
Объясняют падение тем, что «один поток увеличил массив, второй вышел за границы». Массив в Java длину не меняет, расширяется только ArrayList, и подмена одного другим сразу показывает, что механику кандидат не держит в голове.
Синхронизируют запись и оставляют чтение голым. Замок работает только когда его берут обе стороны, иначе читатель спокойно достаёт недописанное значение или вообще не видит изменений.
Берут CopyOnWriteArrayList как универсальную защиту и путают потокобезопасность коллекции с потокобезопасностью операции. На частых вставках это копия массива на каждый вызов, а if (!list.contains(x)) list.add(x) остаётся гонкой даже у синхронизированного списка: защищён отдельный вызов, а не пара.