Detecting concurrent modifications?
在我正在处理的多线程应用程序中,我们有时会在列表中看到
更新:是的,我知道 第二次更新如果有人想像Jason一样提到提到的synchronizedCollection和克隆,再加上像jacekfoo和Javamann这样提到的apache collections框架,我可以接受一个答案。 根据您的更新频率,我的最爱之一是CopyOnWriteArrayList或CopyOnWriteArraySet。他们在更新时创建新的列表/集,以避免并发修改异常。 您最初的问题似乎是要求一个迭代器,该迭代器在保持线程安全的同时查看对基础集合的实时更新。在一般情况下,这是一个难以解决的昂贵问题,这就是为什么标准集合类都不做的原因。 有很多方法可以部分解决问题,在您的应用程序中,其中一种可能就足够了。 Jason提供了一种特定的方法来实现线程安全,并避免引发ConcurrentModificationException,但这只是以牺牲活动性为代价。
Javamann在 如果您已经在使用某些Apache Commons库,那么正如jacekfoo指出的那样,apache collections框架包含一些有用的类。 您也可以考虑查看Google收藏夹框架。 是的,您必须同步对集合对象的访问。 或者,您可以在任何现有对象周围使用同步包装器。请参见Collections.synchronizedCollection()。例如:
但是,所有代码都必须使用安全版本,即使在另一个线程修改时进行迭代也会导致问题。 要解决迭代问题,请首先复制列表。例:
对于更优化的,线程安全的集合,还请查看java.util.concurrent。 请查看java.util.concurrent,了解旨在更好地处理并发性的标准Collections类的版本。 通常,如果要在迭代过程中尝试从列表中删除一个元素,则会收到ConcurrentModificationException。 最简单的测试方法是:
我不确定您将如何解决。您可能必须实现自己的线程安全列表,或者可以创建原始列表的副本进行写入,并具有一个写入列表的同步类。 您还可以同步列表中的Iteratin。
这将锁定列表,使其处于同步状态,并阻止所有在尝试编辑或迭代列表时尝试访问该列表的线程。缺点是您会创建瓶颈。 这样可以通过.clone()方法节省一些内存,并且可能会更快,具体取决于您在迭代中正在执行的操作... 参见实施。它基本上存储一个int:
并且在存在"结构修改"(例如remove)时增加。如果迭代器检测到modCount已更改,则它将引发并发修改异常。 同步(通过Collections.synchronizedXXX)不会做得很好,因为它不能保证迭代器的安全性,它只能通过put,get,set来同步读写操作。 请参见java.util.concurennt和apache collections框架(当一些读取(未同步)多于写入时,它具有一些经过优化的类可以在并发环境中正常工作-请参见FastHashMap。 ConcurrentModificationException是尽力而为的,因为您要问的是一个难题。除了证明您的访问模式不会同时修改列表之外,没有在不牺牲性能的情况下可靠地执行此操作的好方法。 同步可能会阻止并发修改,这最终可能是您要采取的措施,但最终可能会付出高昂的代价。最好的办法可能是坐下来思考一下您的算法。如果您无法提出无锁解决方案,则请使用同步。 在同步块中包装对集合的访问是执行此操作的正确方法。在处理跨多个线程共享的状态时,标准的编程实践要求使用某种锁定机制(信号量,互斥量等)。 但是,根据您的用例,通常可以进行一些优化以仅在某些情况下锁定。例如,如果您有一个经常被读取但很少被写入的集合,那么您可以允许并发读取,但是只要正在进行写入就强制执行锁定。如果集合正在被修改,并发读取只会导致冲突。
您可以尝试使用防御性复制,以便对一个 这将消除您的并发修改异常。但是我不会讲效率;)
Collections.synchronizedList()将名义上呈现一个线程安全的列表,并且java.util.concurrent具有更强大的功能。 |