java.util.AbstractList$Itr.checkForCommodification 三重事件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5525787/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 11:32:42  来源:igfitidea点击:

java.util.AbstractList$Itr.checkForComodification Triple Events

javaexceptionconcurrentmodification

提问by Travis

I run a server and it has an event handler that handles a timing system When I run 3 of them in a row, it gives this exception

我运行一个服务器,它有一个处理计时系统的事件处理程序当我连续运行其中 3 个时,它给出了这个异常

Exception in thread "Thread-8" java.util.ConcurrentModificationException
        at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
        at java.util.AbstractList$Itr.next(AbstractList.java:343)
        at EventManager.run(EventManager.java:77)
        at java.lang.Thread.run(Thread.java:662)

here's the method that the issue is coming from:

这是问题来自的方法:

EventManager.getSingleton().addEvent( new Event() { 
                public void execute(EventContainer c) {
                    p.createProjectile(p.absY, p.absX, offsetY, offsetX, 1166, 43, 31, 70, p2.playerId);
                    c.stop(); // stops the event from running
                }
            }, 950); // in ms (1 second = 1000 ms)
EventManager.getSingleton().addEvent( new Event() { 
                public void execute(EventContainer c) {
                    p2.applyDAMAGE(misc.random(25));
                    c.stop(); // stops the event from running
                }
            }, 1300); // in ms (1 second = 1000 ms)
            p.secondsTillNextDfsSpecial = 120;
EventManager.getSingleton().addEvent( new Event() { 
                public void execute(EventContainer c) {
                    p.secondsTillNextDfsSpecial--;
                    if (p.secondsTillNextDfsSpecial == 0) {
                        p.canPerformDfsSpecial = true;
                        c.stop(); // stops the event from running
                    }
                }
            }, 1000); // in ms (1 second = 1000 ms)



import java.util.ArrayList;
import java.util.List;

/**
 * Manages events which will be run in the future.
 * Has its own thread since some events may need to be ran faster than the cycle time
 * in the main thread.
 * 
 * @author Graham
 *
 */
public class EventManager implements Runnable {


/**
 * A reference to the singleton;
 */
private static EventManager singleton = null;

/**
 * A list of events that are being executed.
 */
private List<EventContainer> events;

/**
 * Initialise the event manager.
 */
private EventManager() {
    events = new ArrayList<EventContainer>();
}

/**
 * The event manager thread. So we can interrupt it and end it nicely on shutdown.
 */
private Thread thread;

/**
 * Gets the event manager singleton. If there is no singleton, the singleton is created.
 * @return The event manager singleton.
 */
public static EventManager getSingleton() {
    if(singleton == null) {
        singleton = new EventManager();
        singleton.thread = new Thread(singleton);
        singleton.thread.start();
    }
    return singleton;
}

/**
 * Initialises the event manager (if it needs to be).
 */
public static void initialise() {
    getSingleton();
}

/**
 * The waitFor variable is multiplied by this before the call to wait() is made.
 * We do this because other events may be executed after waitFor is set (and take time).
 * We may need to modify this depending on event count? Some proper tests need to be done.
 */
private static final double WAIT_FOR_FACTOR = 0.5;

@Override
/**
 * Processes events. Works kinda like newer versions of cron.
 */
public synchronized void run() {
    long waitFor = -1;
    List<EventContainer> remove = new ArrayList<EventContainer>();

    while(true) {

        // reset wait time
        waitFor = -1;

        // process all events
        for(EventContainer container : events) {
            if(container.isRunning()) {
                if((System.currentTimeMillis() - container.getLastRun()) >= container.getTick()) {
                    container.execute();
                }
                if(container.getTick() < waitFor || waitFor == -1) {
                    waitFor = container.getTick();
                }
            } else {
                // add to remove list
                remove.add(container);
            }
        }

        // remove events that have completed
        for(EventContainer container : remove) {
            events.remove(container);
        }
        remove.clear();

        // no events running
        try {
            if(waitFor == -1) {
                wait(); // wait with no timeout
            } else {
                // an event is running, wait for that time or until a new event is added
                int decimalWaitFor = (int)(Math.ceil(waitFor*WAIT_FOR_FACTOR));
                wait(decimalWaitFor);
            }
        } catch(InterruptedException e) {
            break; // stop running
        }
    }
}

/**
 * Adds an event.
 * @param event The event to add.
 * @param tick The tick time.
 */
public synchronized void addEvent(Event event, int tick) {
    events.add(new EventContainer(event,tick));
    notify();
}

/**
 * Shuts the event manager down.
 */
public void shutdown() {
    this.thread.interrupt();
}

}</code></pre>

回答by pajton

Ok, I see two problems:

好的,我看到两个问题:

  1. Your events Listis not synchronized and you are accessing it from different threads (one in EventManagerand second in the first piece of code with addEvent()).

  2. In this loop:

    // process all events
    for(EventContainer container : events) {
        ...
    }
    
  1. 您的事件List未同步,并且您正在从不同的线程访问它(EventManager第一段代码中的一个和第二个addEvent())。

  2. 在这个循环中:

    // process all events
    for(EventContainer container : events) {
        ...
    }
    

you are iterating over events Listand you cannot addnew elements to it while iteration. I assume addEvent()is adding new elements to this list, so basically you shouldn't call it during this iteration.

您正在迭代事件,List并且在迭代时不能向其中添加新元素。我假设addEvent()正在向这个列表添加新元素,所以基本上你不应该在这个迭代中调用它。

Both of this problems can be solved by using CopyOnWriteArrayListwhich enables safe access by concurrent threads andsafely adding new elements during iteration (however new elements will be "visible" only in next iteration).

这两个问题都可以通过使用CopyOnWriteArrayList来解决,它允许并发线程安全访问在迭代期间安全地添加新元素(但是新元素仅在下一次迭代中“可见”)。

Solution:

解决方案:

private EventManager() {
    events = new CopyOnWriteArrayList() ;
}