You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Slick2D游戏观察者模式触发ConcurrentModificationException排查

Fixing ConcurrentModificationException in Your Slick2D Diablo-Style Game's Observer Pattern

Alright, let's tackle this issue head-on—this is a classic gotcha with observer patterns in Java when static state creeps into your subject classes. Let's break down what's happening and how to fix it:

Root Cause: Shared Static Observer List

The core problem here is almost certainly that your base Subject class (or the class implementing observer registration) uses a static ArrayList to hold observers. When you mark the observer list as static, every instance of your subject classes (PauseScreen, StatScreen, etc.) shares the exact same list. That's why Player is ending up in PauseScreen's observers even though you only added it to StatScreen—they're using the same collection under the hood.

When the "Start" button triggers an event, one of these subjects is iterating over the observer list to send notifications, while another component is modifying the list (adding/removing observers). Java's ArrayList throws ConcurrentModificationException when you modify it while iterating over it, which explains the crash only when Player is registered to StatScreen.

Step-by-Step Fixes

1. Remove the Static Modifier from the Observer List

Go to your base Subject class and change the observer collection from static to instance-level. For example:

// Before (bad: shared across all subjects)
public class Subject {
    private static List<Observer> observers = new ArrayList<>();

    public void addObserver(Observer o) {
        observers.add(o);
    }
}

// After (good: each subject has its own list)
public class Subject {
    private List<Observer> observers = new ArrayList<>();

    public void addObserver(Observer o) {
        observers.add(o);
    }
}

This ensures PauseScreen's observers are completely separate from StatScreen's, so Player won't accidentally end up in the wrong list.

2. Make Observer Notifications Thread-Safe

Even with separate lists, game code often runs on multiple threads (e.g., input handling, rendering), which can still cause concurrent modification issues. Fix this in one of two ways:

  • Iterate over a copy of the list: When notifying observers, create a copy of the list first so modifications to the original don't affect the iteration:

    public void notifyObservers(Event event) {
        // Create a copy to avoid ConcurrentModificationException during iteration
        for (Observer observer : new ArrayList<>(observers)) {
            observer.onEvent(event);
        }
    }
    
  • Use a thread-safe collection: Replace ArrayList with CopyOnWriteArrayList, which is designed for concurrent access. It creates a new copy of the list whenever it's modified, so iterators always work on a snapshot:

    private List<Observer> observers = new CopyOnWriteArrayList<>();
    

3. Validate Your Registration Logic

Double-check where you're registering Player as an observer. Ensure you're only calling:

statScreen.addObserver(player);

And not accidentally calling pauseScreen.addObserver(player) somewhere in your code. Adding a debug log in the addObserver method can help confirm this:

public void addObserver(Observer o) {
    System.out.printf("Added observer %s to %s%n", o.getClass().getSimpleName(), this.getClass().getSimpleName());
    observers.add(o);
}

This will print exactly which observer is being added to which subject instance, so you can spot any incorrect registrations.

4. Clean Up Observers When Components Are Disposed

To avoid memory leaks and unexpected behavior, make sure you remove observers when screens are switched (e.g., when closing StatScreen or PauseScreen):

public void removeObserver(Observer o) {
    observers.remove(o);
}

Call this method when a screen is no longer active, like when transitioning from StatScreen back to the main game.

Final Notes

Once you fix the static list issue, the ConcurrentModificationException should disappear, and your component communication will stay decoupled without cross-contamination between screens. Always remember: static state is easy to misuse in game dev—reserve it only for truly global state, not per-instance collections like observers.

内容的提问来源于stack exchange,提问作者hiddenfeld

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:00:25