Какие проблемы могут возникнуть при многопоточном доступе к массиву
Что проверяют
Знаете ли вы модель памяти Java применительно к массивам, а не общие слова про гонки. Типичный уточняющий вопрос: помогает ли volatile int[] и чем заменить обычный массив, если в него пишут несколько потоков.
Как отвечать
Назовите проблемы по отдельности, у каждой своё лечение:
- потерянные обновления:
arr[i]++это чтение, сложение и запись. Два потока прочитали одно значение, один инкремент пропал; - видимость: элементы массива не
volatile, и без happens-before (synchronized,Lock, запись вvolatile-поле) поток может не увидеть запись другого; - несогласованное состояние: инвариант охватывает несколько ячеек, и читатель видит половину обновления;
longиdouble: по JLS запись 64-битных значений безvolatileне обязана быть атомарной, хотя на 64-битных JVM это на практике не проявляется;- false sharing: потоки пишут в соседние ячейки из одной кеш-линии (обычно 64 байта), и производительность проседает без всякой логической гонки.
Инструменты: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray дают атомарные операции над отдельными элементами, VarHandle через MethodHandles.arrayElementVarHandle() даёт то же с выбором режима доступа, для счётчиков под высокой конкуренцией лучше LongAdder. Если меняется несколько ячеек сразу, нужна блокировка. Отдельно упомяните ArrayList: при параллельном add во время расширения внутреннего массива теряются элементы, появляются null или ArrayIndexOutOfBoundsException. Для редких записей подходит CopyOnWriteArrayList, при частых данные лучше разбить по потокам и объединить в конце.
Где спотыкаются
Считают, что volatile int[] arr делает элементы volatile. Volatile только ссылка на массив, записи в ячейки остаются обычными.
Рассчитывают на ConcurrentModificationException как на детектор гонки. Проверка fail-fast работает по принципу best effort: исключения может не быть, а данные уже испорчены.
Предлагают CopyOnWriteArrayList для всего подряд. При частой записи он копирует массив целиком на каждое изменение.