Как имитировать ConcurrentModificationException в собственном классе?

Я читал Пункт 60 "Эффективная Java", а именно: "Рекомендуем использовать стандартные исключения".

Еще одно исключение общего назначения, о котором стоит знать, — это ConcurrentModificationException. Это исключение следует вызывать, если объект, предназначенный для использования одним потоком или с внешней синхронизацией, обнаруживает, что он одновременно изменяется.

Обычно люди сталкиваются с CME, когда пытаются удалить из коллекции во время цикла.

Но здесь меня интересует, что может быть кратким примером обнаружения одновременной модификации самореализуемого объекта класса?

Я ожидаю, что это будет что-то вроде синхронизации внутреннего объекта и соответствующего логического флага, если другой поток сталкивается с ложным флагом, а затем генерирует исключение.

Для простого исследования я нашел в источнике ArrayList:

 final void checkForComodification() {
     if (modCount != expectedModCount)
         throw new ConcurrentModificationException();
 }

но принцип, лежащий в основе того, как поддерживается modCount. Я не могу найти, где он уменьшается.


person Nikolay Kuznetsov    schedule 29.03.2013    source источник
comment
например, в add у вас есть эта строка: ensureCapacityInternal(size + 1); // Increments modCount!!   -  person assylias    schedule 29.03.2013
comment
@assylias, я ожидал отслеживать количество потоков, использующих его. Как только поток перестанет его использовать, число должно быть уменьшено. Но теперь я понимаю, что это количество модификаций.   -  person Nikolay Kuznetsov    schedule 29.03.2013


Ответы (3)


Что касается того, как вы могли бы реализовать такое поведение самостоятельно: ваш класс зависит от некоторых предположений для правильной работы, которые могут быть нарушены, если доступ к объекту не синхронизирован должным образом. Попробуйте проверить эти предположения и бросьте CME, если обнаружите, что они не соответствуют действительности.

В примере ArrayList предполагается, что никто не изменит структуру списка, пока вы выполняете итерацию, поэтому ArrayList отслеживает количество модификаций, которое не должно изменяться во время итерации.

Однако эта проверка предназначена только для большей вероятности того, что плохой доступ вызовет чистое исключение, а не странное поведение. Другими словами, это исключение просто помогает разработчикам и не требует обеспечивать правильность, потому что правильность уже скомпрометирована, когда вы сталкиваетесь с ней.

Это хорошая идея предложить такую ​​​​помощь, когда она не сильно влияет на производительность вашего класса, но с использованием, например. синхронизация для обеспечения правильного использования, вероятно, плохая идея - тогда вы могли бы также сделать класс потокобезопасным для начала.

Вот почему в документации API для ArrayList говорится:

Обратите внимание, что безотказное поведение итератора не может быть гарантировано, так как, вообще говоря, невозможно дать какие-либо жесткие гарантии при наличии несинхронизированной параллельной модификации. Отказоустойчивые итераторы генерируют исключение ConcurrentModificationException в максимально возможной степени. Поэтому было бы неправильно писать программу, корректность которой зависела бы от этого исключения: безотказное поведение итераторов следует использовать только для обнаружения ошибок.

person Medo42    schedule 29.03.2013

Счетчик модификаций — это, по сути, "номер редакции" состояния коллекции. Он увеличивается каждый раз, когда в коллекции происходят структурные изменения. Он никогда не уменьшается.

При запуске итерации итератор запоминает значение счетчика модов. Затем он периодически проверяет, что текущее количество модификаций контейнера по-прежнему равно запомненному значению. Если это не так — это означает, что с момента начала итерации произошли структурные изменения — выдается ConcurrentModificationException.

person NPE    schedule 29.03.2013
comment
Спасибо за объяснение этой части. Итак, это общий подход, если я хочу реализовать собственный объект и выдать исключение, когда два потока пытаются одновременно вызвать его методы? - person Nikolay Kuznetsov; 29.03.2013
comment
@NikolayKuznetsov: Я думаю, что этот метод довольно общий и может использоваться для обнаружения изменений в самых разных обстоятельствах. - person NPE; 29.03.2013

Итак, общий подход следующий: просто запоминайте текущее состояние вашего объекта и проверяйте его состояние каждый раз при попытке доступа к объекту, если состояние изменилось, выбрасывайте свое исключение!

person Gunnarr    schedule 29.03.2013