【问题标题】:Concurrent container access?并发容器访问?
【发布时间】:2014-03-23 01:14:27
【问题描述】:

现在我正在用 Java 试验一些基本的游戏概念。目前,它真正所做的只是显示两个框:一个是玩家可以用箭头键移动的,另一个是随机抖动的“敌人”。现在,我正在修复玩家在单击鼠标左键时向鼠标方向射击小方块的能力。由于它现在的结构,我有四个类 - 一个持有“游戏”逻辑的 JPanel,一个持有该面板的 JFrame,一个实体类,它只是具有位置、大小和速度的物理对象的简单表示移动它的方法,以及一个 Projectile 类,该类使用特殊的 move 方法扩展 Entity 类,该方法将它沿着从给定 Projectile 构造函数的初始点和目标点计算的路径移动一步。

我遇到的问题是尝试更新弹丸的位置。我有一个计时器正在运行以更新非玩家控制对象的位置,并且 JPanel 的 actionPerformed() 方法必须调用每个现有射弹的 move() 方法。现在,当创建一个射弹时,我只是将它放入一个任意大的射弹数组中,确保当数组已满时从 [0] 开始。 actionPerformed() 方法可以与鼠标动作侦听器同时安全地作用于这个数组,但由于我正在研究这个更多的是为了学习而不是解决一个简单的解决方案,我想知道是否有更好的方法来做这不仅仅是拥有一个大小为 [999] 的数组。起初我尝试使用 ArrayList,但显然以并发访问错误告终。我尝试在一个数组中排列射弹,然后在 actionPerformed() 方法中将它们添加到 ArrayList 中,但这给了我空指针错误。

(TLDR) 所以我的问题是:是否有任何 Java 容器(set、map、queue 等)支持并发访问?我认为我不需要一个顺序很重要的容器,因为我必须遍历容器中的每个元素,我只需要一个动态大小的容器,它允许一个线程添加对象,另一个线程同时删除/调用对象。

【问题讨论】:

  • 您可以为此使用CopyOnWriteArrayList
  • 查看java.util.concurrent包中的所有类。
  • 这就是我的想法,但我不确定这是否是解决这个问题的流行选择。 Java 文档指出的性能成本是否明显?
  • 查看 Concurrent 集合而不是 CopyOn 集合以提高性能。

标签: java arrays multithreading swing concurrency


【解决方案1】:

“我想知道是否有比仅使用大小为 [999] 的数组更好的方法来做到这一点。我一开始尝试使用 ArrayList,但显然最终出现了并发访问错误。”

您可以使用ArrayList。您可能会得到一个ConcurrentModificationException,因为您可能在循环遍历它时试图从ArrayList 中删除一个对象。可以看到类似的问题here

解决方案是使用Iterator 而不是循环来遍历对象。

来自Iterator API

  • 迭代器允许调用者在具有明确定义的语义的迭代期间从底层集合中删除元素。

然后你可以使用Iterator.remove() 方法,当你想从ArrayList 中删除一个对象时。所以说你有Projectiles你有一个ArrayList,你可以这样做这个

List<Projectile> projectiles = new ArrayList<>();
...
Iterator it = projectiles.iterator();
while (it.hasNext()) {
    Projectile projectile = (Projectile)it.next();
    if (projectile.hitEnemy()) {
        it.remove();
    }
}

it.remove() 在迭代中删除当前的projectile。无法在 for each 循环中完成的操作,如您将获得 ConcurrentModificationException

您可以在The Collection Interface 教程中查看更多如何使用迭代器。你可以在this thread阅读更多关于ConcurrentModificationException的信息


参见this answer,其中,在遍历Timer 中的List 时,如果Asteroid 不在屏幕上,则通过iterator.remove() 方法将其从List 中动态删除。

【讨论】:

    【解决方案2】:

    如果您正在寻找并发安全容器,请尝试ConcurrentLinkedQueueConcurrentHashMap

    如果您稍后发现您需要对游戏中处理实体的方式进行一些固有的排序,您可能需要使用PriorityQueue

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-05
      • 1970-01-01
      相关资源
      最近更新 更多