Я пишу приложение с графическим интерфейсом пользователя, используя шаблон Графический интерфейс * Immediate Mode , и пользовательский интерфейс работает в потоке, отдельном от механизма, который обеспечивает фактическую функциональность приложения. Поток GUI завершает итерацию по многим спискам объектов, которые концептуально «принадлежат» потоку движка, и эти списки меняются крайне редко. Поток графического интерфейса vsync'ed означает, что он работает на частоте около 60 Гц, а поток двигателя работает на частоте около 200 Гц.
Иногда действия в пользовательском интерфейсе изменяют содержимое коллекций в движке, и у меня есть система передачи сообщений, чтобы публиковать Runnables в потоке движка для выполнения этих мутаций, чтобы гарантировать, что эти мутации не конфликтуют с тем, что происходит в двигателе. Таким образом, я могу гарантировать, что движок всегда видит согласованное представление данных, что для моего приложения очень важно.
Поскольку механизм отвечает за все мутации данных, иногда случается, что механизм изменяет содержимое коллекции во время итерации через GUI, и поскольку эти коллекции являются стандартными коллекциями Java, это предсказуемо и правильно выдает ConcurrentModificationException
. Я могу придумать несколько способов решения этой проблемы на высоком уровне:
- блокировка с использованием синхронизированной коллекции или блокировки чтения-записи
- двойной буфер данных, которые читает поток GUI, и поток GUI переворачивает двойной буфер, когда он завершает рисование кадра
- игнорировать CME и прервать рисование остальной части кадра, что будет извлекать частичную информацию для кадра, в котором происходит «плохая» мутация, и просто перейти к следующему кадру
Блокировка сопряжена со значительным ухудшением производительности, и хотя графический интерфейс пользователя иногда останавливается в ожидании получения блокировки из потока двигателя, очень важно, чтобы поток двигателя работал с постоянной скоростью, и даже блокировка R / W может привести к остановке двигателя. Двойная буферизация сопряжена со значительной сложностью, поскольку в каждом фрейме имеется много данных, которые считываются графическим интерфейсом.
Я даю вам все эти предыстории, потому что я знаю, что вариант 3 уродлив, и что мой вопрос в некотором смысле "не правильный вопрос". Javadoc для ConcurrentModificationException
даже говорит:
Обратите внимание, что отказоустойчивое поведение не может быть гарантировано, так как, вообще говоря, невозможно сделать какие-либо жесткие гарантии при наличии несинхронизированной параллельной модификации. Отказоустойчивые операции создают исключительную ситуацию ConcurrentModificationException. Следовательно, было бы неправильно писать программу, которая зависела от этого исключения для его корректности: исключение ConcurrentModificationException следует использовать только для обнаружения ошибок.
Но! Я не обеспокоен правильностью GUI для единственного кадра, который был бы испорчен CME. Меня интересует только то, что происходит в следующем кадре. Это приводит к моему вопросу: безопасно ли продолжать использовать коллекцию Java (меня больше всего интересует ответ для ArrayList
и HashMap
) после того, как ConcurrentModificationException
было выброшено из его итератора? Кажется логичным, что это так, но я не могу найти документацию, в которой говорится, что объект все еще будет в пригодном для использования состоянии после выброса CME. Очевидно, что в этот момент итератор является тостом, но я бы хотел проглотить исключение и продолжить использование коллекции.