Java - KeyListener的线程导致并发修改。解?

时间:2011-11-22 01:17:22

标签: java concurrency arraylist keylistener

我的游戏更新循环经常迭代ArrayList,我还有一个KeyListener,当按下按钮时,它会向Object添加ArrayList,似乎导致并发修改异常。

经过大量的调查后,我决定最好尝试(我是Java的新手)找出一种方法将关键事件合并到主线程中。鉴于这种情况,我该怎么做,或者有没有办法做到这一点,我没有看到?感谢。

PS:我可以研究更高级的方法,但我宁愿尝试保持相当单线程,并将关键事件传递给主线程。

3 个答案:

答案 0 :(得分:2)

我假设您正在运行一个事件循环,您希望将事件添加到正文的末尾。如果是这样,您可能希望使用java.util.Queue实现,例如LinkedList并执行

while((event=queue.poll()) != null) { 
  /* process event */ 
  queue.add(keyEvent);
}

答案 1 :(得分:2)

解决方案1:

请记住,在迭代过程中,您的密钥监听器将被阻止(这也意味着您的迭代器在旧数据上运行,与解决方案2一样)。

如果使用ArrayList,请执行以下操作:

List list = Collections.synchronizedList(new ArrayList());

并在每个地方使用此list对象。你必须导入

import java.util.Collections;

我同意@msandiford添加如何迭代......

while(some_condition) {
do something
  synchronized(list) {
    Iterator i = list.iterator();
    while (i.hasNext())
        do_something_with(i.next());
  }
  do something else
}

如果你有迭代的外部循环,这是快速解决方案。因此,您暂时不在synchronized块中,以便KeyListener可以添加到数组列表中。

解决方案2:

如果你想使用COW,请注意每当发生添加/更新操作时,它都会复制下面的集合,你的迭代器将不会看到更改。但是不会阻止关键监听器(但此时将在下面创建一个新副本)。

import java.util.concurrent.CopyOnWriteArrayList;


List list = new CopyOnWriteArrayList<your_object_type>();

对于迭代:

while(some_condition) {
  do something
  Iterator i = list.iterator();
  while (i.hasNext())
    do_something_with(i.next());

  do something else
}

解决方案3:

这将是轻微的设计变更。它类似于解决方案2,但只有在您只进行添加操作时才有意义。所以你可以做的是创建另一个临时List并在Keylistener中添加到此列表中。一旦你的迭代通过make synchronized阻止并将所有对象从temp List移动到List,你就会用于迭代。这不会阻止您的KeyListener,但迭代器将看到解决方案2中的旧数据。与解决方案2相比,它可能具有更好的性能。

因此,选择对您的设计有意义的解决方案。

价:

Java Collections COW

答案 2 :(得分:1)

对于事件队列,我通常使用ConcurrentLinkedQueue ,因为队列非常适合此模型(比List / ArrayList好得多)。

队列更容易以这种线程方式使用,因为在列表意义上没有“迭代”队列:只需按下一个项目,抓取一个项目。这两个操作都可以是原子操作(并且相对容易同步)。

我更倾向于使用假设/要求来处理此模型,一旦从队列中消耗了一个对象,它就可以被该线程视为“拥有”(将对象置于其中的线程应该放弃该对象,直到它获得对象为止) ,通过另一个队列,如果有的话。)

  

的ConcurrentLinkedQueue:

     

基于链接节点的无界线程安全队列。这个队列命令元素FIFO(先进先出)...当许多线程共享对公共集合的访问权限时,ConcurrentLinkedQueue是一个合适的选择[但对于2来说“很好”... ...

CopyOnWriteArrayList是另一种可以使用的结构,尽管它更像是一种“通用”结构。我不确定哪个“更快”,但考虑到我从未遇到过ConcurrentLinkedQueue的问题,这是我的第一个主要选择,原因如上所述。还有一些不同的并发安全队列实现,包括绑定队列,也可以在concurrent包中找到。

当然,要保持“旧学校”,只需在同一个对象上同步 ,1)修改(或迭代)EDT中的集合(无论何时发生)2)迭代集合在游戏线程中。这将使操作互斥并防止此异常。一个“缺点”是,这可能会阻止UI线程,具体取决于循环保持锁定的时间。这种方法也可以用于普通的非同步队列。

快乐的编码。