← Назад к вопросам
Сталкивался ли с ConcurrentModificationException
2.0 Middle🔥 141 комментариев
#Другое
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
ConcurrentModificationException в Java
Да, я неоднократно сталкивался с этим исключением, особенно на ранних этапах разработки многопоточного кода. Это одна из самых частых ошибок при работе с коллекциями.
Что это такое?
ConcurrentModificationException выбрасывается, когда коллекция модифицируется во время итерации по ней. Это происходит как в однопоточной среде, так и в многопоточной.
Типичные сценарии
Сценарий 1: Удаление элемента во время итерации
List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie"));
// ❌ Вызывает ConcurrentModificationException
for (String name : names) {
if (name.equals("Bob")) {
names.remove(name); // Изменение списка во время итерации
}
}
Решение 1: Использование Iterator
List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie"));
// ✅ Правильный способ
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
String name = iterator.next();
if (name.equals("Bob")) {
iterator.remove(); // Безопасное удаление
}
}
Решение 2: Создание нового списка
List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie"));
// ✅ Удаляем элементы через фильтрацию
names = names.stream()
.filter(name -> !name.equals("Bob"))
.collect(Collectors.toList());
Решение 3: Копирование и итерация по копии
List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie"));
// ✅ Итерируем по копии, модифицируем оригинал
for (String name : new ArrayList<>(names)) {
if (name.equals("Bob")) {
names.remove(name);
}
}
Сценарий 2: Многопоточная модификация
List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie"));
// ❌ Опасно в многопоточной среде
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.submit(() -> {
for (String name : names) {
System.out.println(name);
}
});
executor.submit(() -> {
names.remove(0); // Другой поток изменяет список
});
Решение: Использование потокобезопасных коллекций
// ✅ CopyOnWriteArrayList
List<String> names = new CopyOnWriteArrayList<>(Arrays.asList("Alice", "Bob", "Charlie"));
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.submit(() -> {
for (String name : names) {
System.out.println(name);
}
});
executor.submit(() -> {
names.remove(0); // Безопасно
});
Практический пример: Обработка событий
public class EventManager {
private List<EventListener> listeners = new CopyOnWriteArrayList<>();
public void subscribe(EventListener listener) {
listeners.add(listener);
}
public void unsubscribe(EventListener listener) {
listeners.remove(listener);
}
public void notify(Event event) {
// ✅ Безопасно — CopyOnWriteArrayList позволяет модифицировать
// список во время итерации
for (EventListener listener : listeners) {
listener.onEvent(event);
}
}
}
Ключевые методы для работы
Iterator.remove()— удаляет последний элемент, полученныйnext()List.removeIf()— удаляет элементы, соответствующие условиюStream API— создаёт новую коллекцию без модификации исходнойCopyOnWriteArrayList— для частых читаний и редких записей в многопоточной средеCollections.synchronizedList()— синхронизированный список (для read-write операций)
Лучшие практики
- Никогда не изменяйте коллекцию во время итерации — используйте Iterator
- Используйте правильные структуры для многопоточности — CopyOnWriteArrayList, ConcurrentHashMap
- Воспользуйтесь Stream API — это более функциональный подход
- Думайте о синхронизации — если разные потоки читают и пишут, нужна защита
- Тестируйте многопоточный код — используйте инструменты для проверки race conditions
Заключение
ConcurrentModificationException — это защитный механизм Java, который предупреждает об ошибке в логике кода. Правильное использование Iterator, Stream API и потокобезопасных коллекций позволяет избежать этой проблемы и написать надёжный код.