Хостинг серверов Minecraft playvds.com
  1. Вы находитесь в русском сообществе Bukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на русский язык плагины наших собратьев из других стран.
    Скрыть объявление

Помогите ужасный ConcurrentModificationException

Тема в разделе "Разработка плагинов для новичков", создана пользователем DPOH-VAR, 7 окт 2012.

  1. Автор темы
    DPOH-VAR

    DPOH-VAR Старожил Пользователь

    Баллы:
    153
    Skype:
    dpohvar
    Вот такая проблема.
    Кто встречал, как с ней бороться?
    Код:
    java.util.ConcurrentModificationException
        at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
        at java.util.AbstractList$Itr.next(Unknown Source)
        at net.minecraft.server.World.tickEntities(World.java:1149)
        at net.minecraft.server.WorldServer.tickEntities(WorldServer.java:428)
        at net.minecraft.server.MinecraftServer.q(MinecraftServer.java:564)
        at net.minecraft.server.DedicatedServer.q(DedicatedServer.java:213)
        at net.minecraft.server.MinecraftServer.p(MinecraftServer.java:474)
        at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:406)
        at net.minecraft.server.ThreadServerApplication.run(SourceFile:539)
    Сгенерированный отчет: ---- Minecraft Crash Report ----
    Возникает случайным образом во время выполнения скрипта, при взрыве: Varscript 0.3 (видео)
    Причину проследить не удалось, т.к. ошибка выскакивает асинхронно. скрипт продолжает работать.
    После ошибки игрок продолжает находиться на сервере, видит все сообщения, но не принимает изменения блоков. Не может писать в чат. Фактически, игрок не меняет своего положения на сервере.
    Консоль сервера отвечает на команды, можно сделать reload, но он ничего не меняет.
    Новые игроки не могут подключиться.
     
    Сникерсни нравится это.
  2. Хостинг MineCraft
    <
  3. Zeluboba

    Zeluboba Участник Девелопер

    Баллы:
    43
    Имя в Minecraft:
    Zeluboba
    не в том разделе.
     
  4. mayor123

    mayor123 Старожил Девелопер Пользователь

    Баллы:
    173
    Многие операции проводить асинхронно нельзя.
     
    DPOH-VAR нравится это.
  5. Автор темы
    DPOH-VAR

    DPOH-VAR Старожил Пользователь

    Баллы:
    153
    Skype:
    dpohvar
    Какие это операции?
    Есть ли список методов, которые нельзя выполнять асинхронно?
    пс: у меня почти все методы выполняются в разных потоках.
     
  6. mayor123

    mayor123 Старожил Девелопер Пользователь

    Баллы:
    173
    An async thread CAN be used for
    • Scheduler method calls
    • Other tasks which don't access the plugin API
    An async thread MUST be used for
    • Tasks which put the current thread to sleep
      • This includes tasks which block, i.e. network read tasks
    An async thread MUST NOT be used for
    • Tasks which call Bukkit API methods (except thread safe method, see below)
    - wiki.bukkit.org
     
  7. apiocera

    apiocera Активный участник Пользователь

    Баллы:
    78
    Вы же понимаете, что без исходников вам причину не укажут? Если очень навскидку, то напомню, что любые действия с глобальными списками нужно производить только в блоке synchronized, и я бы на эту тему изучал FreezeBox.java, особенно в окрестностях строк 31 и 71.

    Ну и да, если вы итерируетесь по списку, например в цикле for, вы не должны его менять.
     
  8. mayor123

    mayor123 Старожил Девелопер Пользователь

    Баллы:
    173
    Тут даже не в этом-то дело, сам Bukkit не позволяет работать асинхронно с API.
     
  9. apiocera

    apiocera Активный участник Пользователь

    Баллы:
    78
    ...если не соблюдать предосторожностей и не иметь карты минного поля. А она у нас имеется. В частности, можно увидеть, что, скорее всего, FreezeBox.java:71 модифицирует список Tile Entity во время их обновления по тику в World.java:1148-1172. Лично я бы попробовал синхронизировать getWorld().tileEntityList и посмотреть, не будет ли падений.
     
    zuma2 и DPOH-VAR нравится это.
  10. Автор темы
    DPOH-VAR

    DPOH-VAR Старожил Пользователь

    Баллы:
    153
    Skype:
    dpohvar
    пробовал использовать при вызове API synchronized(Bukkit.getServer()){...}
    - приводит к взаимной блокировке (причина в структуре плагина)
    Сейчас попробую вызывать свой Event с параметром Runnable, отлавливать его и исполнять.
     
  11. apiocera

    apiocera Активный участник Пользователь

    Баллы:
    78
    Не надо весь сервер синхронизировать, у вас же падения в одном месте. Или нет?
     
  12. Автор темы
    DPOH-VAR

    DPOH-VAR Старожил Пользователь

    Баллы:
    153
    Skype:
    dpohvar
    Все верно. Но я заранее не знаю, по какому миру нужно провести синхронизацию.
    Класс FreezeBox используется для паузы в потоке, синхронизированной по объекту. В нем не используется Bukkit API
     
  13. mayor123

    mayor123 Старожил Девелопер Пользователь

    Баллы:
    173
    Может покажете код этого класса?
     
  14. Автор темы
    DPOH-VAR

    DPOH-VAR Старожил Пользователь

    Баллы:
    153
    Skype:
    dpohvar
    Код:
    package me.dpohvar.varscript.utils.box;
    /**
    * Created by IntelliJ IDEA.
    * User: DPOH-VAR
    * Date: 12.08.12
    * Time: 23:09
    * To change this template use File | Settings | File Templates.
    */
     
    public class FreezeBox<T> {
        Object o = null;
        boolean melted = false;
        public FreezeBox(){}
        public void freeze(final T freezing){
            if (melted) {
                return;
            }
            if (o!=null) throw new RuntimeException("can't freeze at FreezeBox");
            o=freezing;
            synchronized (o){
                try {
                    while(needWait()){
                        o.wait();
                    }
                } catch (InterruptedException ignored) {
                }
            }
        }
        private boolean needWait(){
            if (melted) return false;
            if (o==null) return false;
            if (!(o instanceof Thread)) return true;
            if (((Thread)o).isInterrupted()) return false;
            return true;
        }
     
        public void freeze(final T freezing,int milliseconds){
            if (melted) return;
            if (o!=null) throw new RuntimeException("can't freeze at FreezeBox");
            o=freezing;
            synchronized (o){
                try {
                    if(needWait()) o.wait(milliseconds);
                } catch (InterruptedException ignored) {
                }
            }
        }
        public void unfreeze(){
            melted=true;
            if (o==null) return;
            Object t=o;
            o=null;
            synchronized (t){
                t.notify();
            }
        }
        public void unfreezeAfter(final int ms){
            new Thread(){
                @Override public void run(){
                    synchronized (this){
                        try {
                            this.wait(ms);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    unfreeze();
                }
            }.start();
        }
    }
    
    Вот основное его действие:

    1. synchronized (o){
    2. try {
    3. if(needWait()) o.wait(milliseconds);
    4. } catch (InterruptedException ignored) {
    5. }
    6. }

      Т.е. обычный wait переданного объекта, но при этом на него не влияют notify, вызванные вне объекта FreezeBox
     
  15. Автор темы
    DPOH-VAR

    DPOH-VAR Старожил Пользователь

    Баллы:
    153
    Skype:
    dpohvar
    Всем спасибо, синхронизация по Bukkit.getServer() решила проблему.
    Вот только не все можно синхронизировать по нему.
    Если у кого еще будет похожая проблема, вот решение:
    Оборачиваем все вызовы Bukkit API в
    Код:
    synchronized(Bukkit.getServer()){
        // Bukkit API
    }
    но нельзя оборачивать вызовы события и любые длительные методы, иначе сервер просто зависнет.
     

Поделиться этой страницей