Требуется проверка: исключение ConcurrentModificationException, в то время как ReentrantLock гарантирует, что это невозможно

#java #thread-safety #concurrentmodification

#java #потокобезопасность #concurrentmodification

Вопрос:

Контекст: Github: выпуск 20 и Github: выпуск 22

У меня есть исключение ConcurrentModificationException, но я не знаю, где искать. Переменная является частной и доступна только в том же классе, где весь доступ к ней заблокирован с помощью ReentrantLock.

Во время выполнения цикла объекты не удаляются (так почему возникает это исключение?)

Это разбитый фрагмент кода:

 private static final TreeMap<Long, TrackerHolder> TRACKER_LIST = new TreeMap<>();
private static final ReentrantLock TRACKER_LIST_LOCK = new ReentrantLock();

public static void tickUntil(long time){
    TRACKER_LIST_LOCK.lock();
    boolean hasWork = true;
    while(System.nanoTime() < time amp;amp; hasWork) {
        hasWork = false;
        for(TrackerHolder holder : TRACKER_LIST.values()){ //This line is giving the error, indicating TRACKER_LIST is being modified somehow.
            Tracker tracker = holder.getTracker();
            if(tracker.needsTick()){
                hasWork = true;
                tracker.grantTick();
            }
        }
    }
    TRACKER_LIST_LOCK.unlock();
}
  

Это проект с открытым исходным кодом, доступ к которому вы можете получить здесь (привязан к текущему моменту времени.)

 java.util.ConcurrentModificationException: null
    at java.util.TreeMap$PrivateEntryIterator.nextEntry(TreeMap.java:1211) ~[?:1.8.0_191]
    at java.util.TreeMap$ValueIterator.next(TreeMap.java:1256) ~[?:1.8.0_191]
    at cf.terminator.tiquality.tracking.TrackerManager.tickUntil(TrackerManager.java:69) ~[TrackerManager.class:?]
    at cf.terminator.tiquality.monitor.TickMaster.onServerTick(TickMaster.java:64) ~[TickMaster.class:?]
    at net.minecraftforge.fml.common.eventhandler.ASMEventHandler_2625_TickMaster_onServerTick_ServerTickEvent.invoke(.dynamic) ~[?:?]
    at net.minecraftforge.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:90) ~[ASMEventHandler.class:?]
    at net.minecraftforge.fml.common.eventhandler.EventBus.post(EventBus.java:746) ~[EventBus.class:?]
    at net.minecraftforge.fml.common.eventhandler.EventBus.post(EventBus.java:696) ~[EventBus.class:?]
    at net.minecraftforge.fml.common.FMLCommonHandler.onPostServerTick(FMLCommonHandler.java:266) ~[FMLCommonHandler.class:?]
    at net.minecraft.server.MinecraftServer.func_71217_p(MinecraftServer.java:712) ~[MinecraftServer.class:?]
    at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:526) [MinecraftServer.class:?]
    at java.lang.Thread.run(Thread.java:748) [?:1.8.0_191]
  

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

Спасибо, что уделили время.

Комментарии:

1. Читая код, мне интересно, возможно ли, что вызов grantTick фактически изменяет коллекцию трекеров?

2. Учитывая, что блокировка является реентерабельной, поэтому ее можно «повторно получить» в том же потоке: есть ли какая-либо возможность, что «tracker. needsTick()» или «tracker.grantTick()» вызывает обратный вызов TrackerManager… Что изменяет состояние карты?

3. В то же время быстрым решением (хотя и с высокой производительностью) может быть копирование структуры перед циклом по ней.

4. Я проверил, что grantTick () и needsTick () никоим образом не изменяют коллекцию.

5. Имейте в виду, что ваш код может никогда не разблокироваться, если он выдает исключение. unlock должно быть в finally блоке.