开发《Asteroids》街机游戏时绘制Ball触发ConcurrentModificationException求助
解决Asteroids游戏中Ball绘制时的ConcurrentModificationException问题
问题根源
你给drawBullets加了synchronized但无效,核心原因是:这个锁仅保证当前方法执行时独占,但若其他线程在遍历过程中修改了bulletsList的结构(比如删除元素),foreach依赖的迭代器是fail-fast机制,只要检测到遍历期间list结构被修改,就会抛出异常。
你提到碰撞时修改Ball会触发问题,大概率是碰撞逻辑里直接调用了bulletsList.remove(ball)这类修改list结构的代码;而其他对象的碰撞处理仅修改对象内部状态(比如设为死亡),未改动list结构,所以没触发异常。
可行解决方案
统一锁所有操作
bulletsList的代码块
不要只给绘制方法加锁,所有读写bulletsList的地方(比如碰撞检测、子弹生成)都要用同一个锁对象同步,同时修改list结构时用迭代器的remove()方法:// 专门的锁对象,比用this更清晰 private final Object bulletsLock = new Object(); private void drawBullets(Graphics g){ synchronized(bulletsLock) { for (Ball ball : bulletsList) { if (ball.getSize() > 0 && ball.isAlive()) { ball.drawBall(g); } } } } // 碰撞处理方法示例 private void handleBulletCollisions() { synchronized(bulletsLock) { Iterator<Ball> iterator = bulletsList.iterator(); while (iterator.hasNext()) { Ball ball = iterator.next(); if (checkCollision(ball)) { // 你的碰撞检测逻辑 iterator.remove(); // 用迭代器删除,不会触发异常 // 或者仅修改ball状态:ball.setAlive(false); } } } }遍历list的副本
如果不想给所有操作加锁,也可以在绘制前复制一份list,遍历副本而非原list,这样原list的修改不会影响遍历:private void drawBullets(Graphics g){ List<Ball> bulletsCopy; // 复制时加锁确保副本完整 synchronized(bulletsList) { bulletsCopy = new ArrayList<>(bulletsList); } // 遍历副本,原list修改不会触发异常 for (Ball ball : bulletsCopy) { if (ball.getSize() > 0 && ball.isAlive()) { ball.drawBall(g); } } }
额外提示
如果仅修改Ball的内部状态(比如isAlive),而非从list中删除,只需保证Ball的状态字段是线程安全的(比如用volatile修饰,或者同步状态修改的代码),就不会触发该异常。但涉及list结构的增删,必须同步所有相关操作,或使用副本遍历。
内容的提问来源于stack exchange,提问作者Maor Krasner
相关产品推荐
相关产品推荐

