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

Space Invaders类游戏初始小行星生成Java报错求助

问题分析

java.util.ConcurrentModificationException 触发的核心原因有两个:要么是遍历集合的同时修改了集合结构(比如直接添加/删除元素),要么是多线程并发操作非线程安全的集合(比如ArrayList)。结合你的场景来看:

  • 构造函数主线程生成小行星时,若定时器提前启动(哪怕是代码顺序问题导致的意外触发),两个线程会同时操作asteroids集合;
  • updateGameState里遍历小行星的过程中直接调用add()添加新小行星,属于遍历期间修改集合结构,必然触发异常。
解决方案

1. 严格控制初始化顺序:先生成初始小行星,再启动定时器

确保主线程完成所有初始小行星的创建后,再启动定时器,避免多线程并发操作集合:

// GamePanel构造函数修正
public GamePanel() {
    // 先完成基础组件初始化
    this.setFocusable(true);
    this.requestFocus();
    // ...其他初始化代码(比如火箭、子弹集合)

    // 第一步:生成初始NUM_OF_ASTEROIDS个小行星
    asteroids = new ArrayList<>();
    for (int i = 0; i < Globals.NUM_OF_ASTEROIDS; i++) {
        // 替换成你的小行星初始化逻辑,比如随机位置、速度
        asteroids.add(new Asteroid(
            (int)(Math.random() * (getWidth() - Asteroid.WIDTH)),
            -Asteroid.HEIGHT,
            Globals.ASTEROID_SPEED
        ));
    }

    // 第二步:启动定时器
    timer = new Timer(Globals.TIMER_DELAY, this);
    timer.start();
}

2. 安全处理遍历中的集合修改

不要在遍历asteroids的过程中直接添加/删除元素,改用迭代器删除,并用临时集合存储需要新增的小行星,遍历结束后批量添加:

private void updateGameState() {
    List<Asteroid> newAsteroids = new ArrayList<>();

    // 使用迭代器遍历,避免遍历期间修改集合触发异常
    Iterator<Asteroid> iterator = asteroids.iterator();
    while (iterator.hasNext()) {
        Asteroid asteroid = iterator.next();
        // 处理小行星移动、碰撞检测逻辑
        asteroid.move();
        
        // 判断是否被击毁
        if (asteroid.isDestroyed()) {
            iterator.remove(); // 用迭代器安全删除元素
            // 记录需要新增的小行星
            newAsteroids.add(new Asteroid(
                (int)(Math.random() * (getWidth() - Asteroid.WIDTH)),
                -Asteroid.HEIGHT,
                Globals.ASTEROID_SPEED
            ));
        }
        // 其他逻辑:比如判断是否超出屏幕边界需要移除
        else if (asteroid.getY() > getHeight()) {
            iterator.remove();
            newAsteroids.add(new Asteroid(...));
        }
    }

    // 遍历结束后,批量添加新小行星到原集合
    asteroids.addAll(newAsteroids);
}

3. 可选:给集合加线程同步(彻底解决多线程并发问题)

如果构造函数和定时器线程仍存在并发操作的可能,用Collections.synchronizedList包装集合,并在所有操作集合的地方加同步锁:

// 初始化线程安全的集合
private List<Asteroid> asteroids = Collections.synchronizedList(new ArrayList<>());

// 遍历和修改时都加同步锁
private void updateGameState() {
    List<Asteroid> newAsteroids = new ArrayList<>();
    
    synchronized (asteroids) {
        Iterator<Asteroid> iterator = asteroids.iterator();
        while (iterator.hasNext()) {
            // ...同上处理逻辑
        }
    }

    synchronized (asteroids) {
        asteroids.addAll(newAsteroids);
    }
}
验证步骤
  1. 替换构造函数的初始生成逻辑,确保先创建小行星再启动定时器;
  2. 调整updateGameState的遍历和新增逻辑;
  3. 启动游戏,检查是否还有异常,同时确认初始小行星正常生成、击毁后能自动补充。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 12:21:02