【问题标题】:Write and read an ArrayList in 2 different Threads在 2 个不同的线程中写入和读取 ArrayList
【发布时间】:2014-07-15 08:54:19
【问题描述】:

我目前正在为学校开发一个小项目,它是一款塔防游戏。

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

现在我想将射弹添加到我的塔中。为此,我需要向我的实体 ArrayList 添加一个新的 Projectile 对象。每当我尝试这样做时,它都会说

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

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

如果有人能帮我解决这个问题,我已经想感谢你了,如果你还在阅读,那就太棒了

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

世界更新方法:

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);
}

【问题讨论】:

  • 不是重点,但很有帮助,请阅读 THREAD LOCAL
  • 如果不看一些代码,很难说发生了什么。但我认为正在发生的事情是,一个线程试图访问您的列表,而另一个线程已经在迭代该列表。所以,也许你必须使用锁来让你的线程并发访问列表。您可以尝试使用为此设计的列表接口,例如 CopyOnWriteArrayList 或 ConcurrentHashMap 。
  • 请显示您的游戏循环。通常java.util.ConcurrentModificationException 发生在您在迭代时删除内容时。这并不意味着您正在从不同的线程访问内容。
  • 现在添加了两个循环,你可以看到我同时迭代
  • @besplash 看,没有线程。看看:javacodegeeks.com/2011/05/… 或我的回答 ;)

标签: java multithreading arraylist


【解决方案1】:

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

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 循环迭代列表时从列表中删除元素。

【讨论】:

  • 与之前相同的异常,但在“Entity e = iterator.next();”这一行上
  • @besplash 你能显示你要添加到列表中的代码吗?
  • Argh.. 我无法在评论中显示代码。它就像entities.add(new Monster(arg01, arg02, arg03));这发生在方法 waveTimer();
  • 那种格式不喜欢我。我花了 10 分钟才做到这一点
【解决方案2】:

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

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

【讨论】:

  • 非常感谢!这个实际上似乎有效。我的射弹目前没有飞行,但这可能是一个不同的问题。至少我的框架很好,没有像以前那样丢失任何物体,而且我没有任何异常。如果它解决了我的问题,我会告诉你,当我的 Projectiles 使用此解决方案时
【解决方案3】:

如果你想使用synchronized,你必须这样做

synchronized(myList) {
    //Do stuff
}

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

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

【讨论】:

    【解决方案4】:

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

    请注意,顾名思义,每次写入都会复制内部 array,因此这在性能方面可能具有挑战性。

    否则,您可能想要更底层并尝试使用ReentrantReadWriteLock

    【讨论】:

      【解决方案5】:

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

      【讨论】:

      • 由于我们没有看到代码,可能只是合适的情况。假设他们只需要将对象添加到列表中,而不是在之前检查它,也不需要做任何更复杂的事情。当然,在任何其他情况下,Jon 在这件事上都是正确的,他们需要一些同步块或类似的东西
      • 我不需要看代码就知道Vector是一个选项。
      • 试图用 Vector 替换我的 ArrayList,但它不起作用,那么我究竟该如何更改我的代码?我认为这与您的(或多或少)有关
      • Vector 不会阻止 ConcurrentModificationException 当一个线程插入到向量中而另一个线程正在对其进行迭代时。这正是 OP 正在经历的。
      • @besplash 如果更换它并没有解决您的问题,那么您的情况可能更复杂,因为 LuiggiMendoza 和 Kenster 在他们的 cmets 中指出。查看您的代码并考虑其中的关键部分,并实现同步块以确保它们在互斥中执行
      猜你喜欢
      • 1970-01-01
      • 2018-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多