Какими средствами Java можно предотвратить состояние гонки
Что проверяют
Различаете ли вы видимость и атомарность и знаете ли, что гарантирует каждый инструмент. Следом обычно дают код с count++ или if (!map.containsKey(k)) map.put(k, v) и просят найти проблему.
Как отвечать
Сначала определение: гонка возникает, когда результат зависит от порядка выполнения потоков, чаще всего в операциях «прочитать, изменить, записать» и «проверить, затем действовать». Дальше инструменты:
synchronized: взаимное исключение на мониторе объекта и видимость: всё, что поток записал до выхода из блока, увидит следующий, кто войдёт по тому же монитору. Просто, но без таймаута и прерывания;ReentrantLock: то же плюсtryLockс таймаутом,lockInterruptibly, честная очередь иCondition.unlock()всегда вfinally. Для частого чтения естьReentrantReadWriteLockиStampedLock;- атомарные классы:
AtomicInteger,AtomicReference,compareAndSetна CAS без блокировок. Счётчику под большой конкуренцией лучше подходитLongAdder; volatile: только видимость и порядок. Флагу остановки хватает, аcount++это три операции, их он не спасает;- конкурентные коллекции:
ConcurrentHashMapс атомарнымиcomputeIfAbsent,merge,putIfAbsent,BlockingQueueдля передачи данных между потоками; - отказ от общего изменяемого состояния: неизменяемые объекты (
finalполя,record), данные в пределах потока (локальные переменные,ThreadLocal), передача сообщений вместо общих полей.
Если у вас виртуальные потоки, добавьте: до Java 24 блок synchronized закреплял виртуальный поток за несущим, поэтому в долгих блокирующих участках советовали ReentrantLock. JEP 491 это ограничение снял.
Где спотыкаются
Считают, что потокобезопасная коллекция делает код потокобезопасным. get, затем put в ConcurrentHashMap это две операции, между ними влезет другой поток, нужен compute или merge.
Ставят volatile на счётчик и считают задачу решённой.
Синхронизируются на разных объектах: на this в разных экземплярах, на объекте, созданном внутри метода, или на Integer, который после автоупаковки уже другой. Взаимного исключения тогда нет.