arraylists的conccurent修改和同步

时间:2015-09-08 20:08:29

标签: java multithreading arraylist

这个问题更多的是关于询问我的做事方式是否是“正确”的方式。我有一些程序涉及不断更新图形组件。为此,我有以下方法。

public void update(){
    for (BaseGameEntity movingEntity : movingEntityList) {
        ((MovingEntity)movingEntity).update();
    }
}

本质上,包含此方法的类包含所有需要更新的图形对象的列表,并循环遍历,调用各自的更新方法。

当我必须添加新实体或从此列表中删除当前实体时,问题就出现了。实体的添加和删除由不同的线程处理,正如您所猜测的,如果我尝试添加/删除实体同时循环并更新其图形组件,则会导致并发修改异常。

我的临时解决方案是简单地在此周围抛出一个try-catch块,并忽略突然出现的任何并发修改异常 - 实际上,不在该特定时间更新。这正是我想要的,没有问题发生。

public void update(){
    try{
        for (BaseGameEntity movingEntity : movingEntityList) {
            ((MovingEntity)movingEntity).update();
        }
    }catch(ConcurrentModificationException e){
            //Do Nothing
    }
}

但是,我的问题是,这是处理这个问题的“正确”方法吗?我是否应该做类似this answer中概述的事情?如果我的错了,处理这个问题的“正确”方法是什么?我并没有专门寻找使我的arraylist线程安全的方法,例如通过同步列表,我特别询问我的方法是否是一个有效的方法,或者是否有某些原因我应该避免它并实际使用同步列表。 / p>

3 个答案:

答案 0 :(得分:2)

正确的方法是将列表与Collections.synchronizedList()

同步
List list = Collections.synchronizedList(new ArrayList());
      ...
  synchronized (list) {
      Iterator i = list.iterator(); // Must be in synchronized block
      while (i.hasNext())
          foo(i.next());
  }

如果您的浏览次数超过更新列表的次数,您还可以使用CopyOnWriteArrayList

如果您不介意偶尔丢失更新(或者如果它们的同步价格很少发生),您的方式就可以了。

答案 1 :(得分:1)

  

这是处理此问题的“正确”方法吗?

如果你不介意以降低错误更新为代价来增加并发性,那么答案就是“是”。如果经常发生对列表的重大添加和删除,您确实存在连续多次未完成更新的风险。

另一方面,当更新频率明显高于添加/删除对象的频率时,这种解决方案听起来很合理。

  

我应该[使用synchronized]吗?

这也是一个可行的解决方案。不同之处在于更新正在进行时更新将无法继续。当调用update的时间很关键时,这可能是不可取的(但在每次调用时更新所有内容并不重要。)

答案 2 :(得分:1)

有些人认为它是所有通用同步问题的副本。我认为情况并非如此。在这个意义上,你要求一个非常具体的星座,以及你的解决方案是否“正常”。

根据您的描述,实际目标似乎很明确:您希望快速并发地迭代实体以调用update方法,并避免使用{{{{}}隐含的任何同步开销1}}或类似的方法。

此外,我认为您提出的解决方案背后的主要思想是Collections#synchronizedList调用必须经常且尽可能快地完成,而添加或删除实体“很少”发生。

因此,与常规操作相比,添加和删除元素是异常; - )

并且(正如his answer中已经指出的那样)这样的设置,捕获和忽略update的解决方案是合理的,但是你应该知道后果。

可能会调用某些实体的ConcurrentModificationException方法,然后由于update而导致循环失效。您应绝对确定,这不会产生不良副作用。例如,根据ConcurrentModificationException实际执行的操作,这可能导致某些实体在屏幕上移动得更快,而其他实体则根本不移动,因为他们的update次调用由于多次update而被错过{1}}。如果添加和删除实体很少发生的操作,这可能会特别成问题:如果一个线程不断添加或删除元素,那么列表的最后一个元素可能永远不会收到ConcurrentModificationExceptions打电话。

如果你想要一些“通过例子证明”:我首先在JUNG Graph Library中遇到过这种模式,例如,在SpringLayout类和其他人中。当我第一次看到它时,我畏缩了一下,因为乍一看它看起来非常黑暗和危险。但在这里,理由是相同的:过程必须尽可能快,并且对图结构的修改(这将导致异常)很少见。请注意,当update发生时,JUNG的人实际上会对相应的方法进行递归调用 - 仅仅是因为他们不能总是假设该方法被另一个线程不断调用。反过来,这个会产生令人讨厌的副作用:如果另一个线程进行常量修改,并且每次调用该方法时都会抛出 ,那么这将以ConcurrentModificationException结束......但幸运的是,情况并非如此。