在2个不同的线程中写入和读取ArrayList

时间:2014-05-26 14:38:02

标签: java multithreading arraylist

我目前正在为学校开展一个小型项目,这是一场TowerDefense游戏。

初始化游戏时,会启动计算所有内容的线程。启动该线程后,我的代码跳转到循环,循环我的图形代码。我认为它正在使用2个线程。一个线程用于计算,另一个线程是“标准”线程,用于打开程序本身。

现在我想将Projectiles添加到我的塔楼。为此,我需要向我的Entity ArrayList添加一个新的Projectile对象。每当我尝试这样做时,它说

线程“main”中的异常java.util.ConcurrentModificationException

并指向我的图形循环所在的行,它试图循环我的实体数组。这是有道理的,因为当我的Projectiles被添加到这个数组时,我的图形循环尝试读取。我尝试使用“synchronized”,我想我知道如何使用它以及它的用途,但我不确定在我的代码中将它用于何处。我从昨天开始尝试解决这个问题,并且无法在互联网上找到任何解决方法。另外我不确定哪部分代码可以帮助你,如果你不明白我的程序是如何工作的,问题是什么,如果你需要什么,请随时问。

如果有人可以帮我解决这个问题会很棒,如果你还在读书,我已经要感谢你了

编辑:我认为这些是重要的代码:

世界更新方法:

private void updateRunning() {
    waveTimer();
    for (Entity e : entities) {
        if (e.isMonster() && !block) {
            e.toMonster().move();
            if (e.toMonster().getHealth() <= 0)
                entities.remove(e);
        } else if (e.isTower()) {
            e.toTower().setTarget(e.toTower().findTarget());
            e.toTower().shoot();
        } else if (e.isProjectile()) {
            e.toProjectile().update();
        }
    }
}

渲染器循环方法:

private void updateRunning() {
    cam.update();
    for (Entity ent : world.getEntities()) {
        ent.draw();
    }
    cursor.update();
    cursor.draw(); // Must always be on bottom draw-methods, to be drawed
                   // on top of
                   // everything else
    cam.refresh();
}

EDIT2

private void addWave() {
  if (wave % 3 == 1) {
    for (int i = 0; i < 6; i++) {
      entities.add(new Monster(this, i + 1, path.getCoord(0).x,  
                   path.getCoord(0).y - i * 57, 0.35f, Assetloader.monster01));
    }
  } else if (wave % 3 == 2) {
  } else {
  }
}

private void attack(Monster target, float damage) {
  world.addEntity(new Projectile(world, vec.x, vec.y, 20, 20, target,  
                this, Assetloader.cursor01));
  ready = false;
  lastShot = System.currentTimeMillis();
}

public void addEntity(Entity e) {
  this.entities.add(e);
}

5 个答案:

答案 0 :(得分:2)

当您需要在多个线程中收集数据时,您应该使用支持并发的结构。在Java中,可以在java.util.concurrent包中找到这样的类。对于您的情况,您应该使用CopyOnWriteArrayList类而不是ArrayList

根据我的经验,使用ArrayListArrayList的并发支持变体不是这些情况的最佳选择。使用像ConcurrentLinkedQueue这样的并发队列会更好。

答案 1 :(得分:0)

根据您的问题看起来,您似乎在多个线程之间共享数据结构。适当的类是java.util.Vector,因为它(或多或少)是ArrayList的同步版本

答案 2 :(得分:0)

您可以使用并发集合包中的CopyOnWriteArrayList来自动同步访问。

请注意,内部array会在每次写入时被复制,顾名思义,因此这可能会对性能产生挑战。

否则,您可能需要更低级别并尝试使用ReentrantReadWriteLock

答案 3 :(得分:0)

如果您想使用synchronized,则必须执行类似的操作

synchronized(myList) {
    //Do stuff
}

因此,当另一个线程在同步块中时,没有其他线程可以访问该列表。 但请注意deadlocks

这里有一个关于synchronized的教程 http://tutorials.jenkov.com/java-concurrency/synchronized.html

答案 4 :(得分:0)

只需使用迭代器功能在迭代时删除项目:

private void updateRunning() {
        waveTimer();
        Iterator<Entity> iterator = entities.iterator();
        while(iterator.hasNext()) {
            Entity e = iterator.next();
            if (e.isMonster() && !block) {
                e.toMonster().move();
                if (e.toMonster().getHealth() <= 0)
                    iterator.remove(e);
            } else if (e.isTower()) {
                e.toTower().setTarget(e.toTower().findTarget());
                e.toTower().shoot();
            } else if (e.isProjectile()) {
                e.toProjectile().update();
            }
        }
    }

这与线程无关,你只是在使用for-each循环迭代它时无法从列表中删除元素。