在我正在研究的多线程应用程序中,我们偶尔会在列表中看到ConcurrentModificationExceptions (主要是ArrayList ,有时是Vectors)。 但是还有一些时候我认为并发修改正在发生,因为遍历集合似乎缺少项目,但没有抛出异常。 我知道ConcurrentModificationException的文档说你不能依赖它,但我怎样才能确保我不会同时修改List? 并且在同步块中包装对集合的每次访问是唯一可以防止它的方法吗?

更新:是的,我知道Collections.synchronizedCollection ,但它并不能防止在您迭代它时修改集合的人。 我认为至少有一些问题发生在我正在迭代它时有人向集合添加内容时。

第二次更新如果有人想结合提到的synchronizedCollection和像Jason那样的克隆,提到java.util.concurrent和像jacekfoo和Javamann这样的apache集合框架,我可以接受一个答案。

===============>>#1 票数:6

根据您的更新频率,我最喜欢的一个是CopyOnWriteArrayList或CopyOnWriteArraySet。 他们在更新时创建新的列表/集以避免并发修改异常。

===============>>#2 票数:4 已采纳

您的原始问题似乎是要求迭代器在保持线程安全的同时查看底层集合的实时更新。 在一般情况下,这是一个非常昂贵的问题需要解决,这就是为什么标准集合类都没有这样做的原因。

有很多方法可以实现问题的部分解决方案,在您的应用程序中,其中一个可能就足够了。

Jason提供了一种实现线程安全的特定方法,并避免抛出ConcurrentModificationException ,但仅以牺牲活力为代价。

Javamann提到了java.util.concurrent包中的两个特定类 ,它们以无锁方式解决了同样的问题,其中可伸缩性至关重要。 这些只与Java 5一起提供,但是已经有各种项目将软件包的功能反向移植到早期的Java版本中,包括这个版本,尽管它们在早期的JRE中不会有这么好的性能。

如果您已经在使用某些Apache Commons库,那么正如jacekfoo所指出的那样apache集合框架包含一些有用的类。

您也可以考虑查看Google集合框架

===============>>#3 票数:3

查看java.util.concurrent,了解设计用于更好地处理并发性的标准Collections类的版本。

===============>>#4 票数:3

是的,您必须同步对集合对象的访问。

或者,您可以使用任何现有对象周围的同步包装器。 请参见Collections.synchronizedCollection() 例如:

List<String> safeList = Collections.synchronizedList( originalList );

但是,所有代码都需要使用安全版本,即使如此迭代,而另一个线程修改也会导致问题。

要解决迭代问题,请先复制列表。 例:

for ( String el : safeList.clone() )
{ ... }

对于更优化的线程安全集合,还要查看java.util.concurrent

===============>>#5 票数:2

通常,如果您在迭代过程中尝试从列表中删除元素,则会收到ConcurrentModificationException。

测试这个的最简单方法是:

List<Blah> list = new ArrayList<Blah>();
for (Blah blah : list) {
     list.remove(blah); // will throw the exception
}

我不确定你是怎么解决它的。 您可能必须实现自己的线程安全列表,或者您可以创建原始列表的副本以进行写入,并具有写入列表的同步类。

===============>>#6 票数:1

您可以尝试使用防御性复制,以便对一个List修改不会影响其他List

===============>>#7 票数:1

在同步块中包装对集合的访问是执行此操作的正确方法。 标准编程实践规定在处理跨多个线程共享的状态时使用某种锁定机制(信号量,互斥量等)。

根据您的使用情况,您通常可以进行一些优化,仅在某些情况下锁定。 例如,如果您有一个经常读取但很少写入的集合,那么您可以允许并发读取,但只要写入正在进行就强制执行锁定。 如果集合正在被修改,则并发读取仅导致冲突。

===============>>#8 票数:1

ConcurrentModificationException是最好的努力,因为你问的是一个难题。 除了证明您的访问模式不会同时修改列表之外,没有好的方法可靠地执行此操作而不牺牲性能。

同步可能会阻止并发修改,它可能是你最终要求的,但它最终可能会成本高昂。 最好的办法是坐下来思考一下你的算法。 如果您无法提出无锁解决方案,那么请求同步。

===============>>#9 票数:1

查看实施。 它基本上存储了一个int:

transient volatile int modCount;

当存在“结构修改”(如删除)时,它会增加。 如果迭代器检测到modCount已更改,则抛出并发修改异常。

同步(通过Collections.synchronizedXXX)将无法正常工作,因为它不保证迭代器安全,它只通过put,get,set来同步写入和读取...

请参阅java.util.concurennt和apache集合框架(当有更多读取(不同步)而不是写入时,它有一些优化的类在并发环境中正常工作 - 请参阅FastHashMap。

===============>>#10 票数:1

您还可以在列表上通过iteratins进行同步。

List<String> safeList = Collections.synchronizedList( originalList );

public void doSomething() {
   synchronized(safeList){
     for(String s : safeList){
           System.out.println(s);

     }
   }

}

这将锁定同步列表并阻止在编辑或迭代它时尝试访问列表的所有线程。 缺点是你创造了一个瓶颈。

这节省了.clone()方法的一些内存,可能会更快,具体取决于您在迭代中所做的事情......

===============>>#11 票数:-1

Collections.synchronizedList()将呈现名义上为线程安全的列表,并且java.util.concurrent具有更强大的功能。

===============>>#12 票数:-1

这将消除您的并发修改异常。 我不会谈论效率;)

List<Blah> list = fillMyList();
List<Blah> temp = new ArrayList<Blah>();
for (Blah blah : list) {
     //list.remove(blah);  would throw the exception
     temp.add(blah);
}
list.removeAll(temp);

  ask by Paul Tomblin translate from so

未解决问题?本站智能推荐:

12回复

并发修改例外

我目前正在研究一个多线程应用程序,偶尔也会收到一个同时修改的异常(平均大约每小时一次或两次,但是看似随机的间隔)。 有缺陷的类本质上是地图的包装器 - 它扩展了LinkedHashMap (将accessOrder设置为true)。 该类有几个方法: set方法将键/值对添加到内
1回复

ConcurrentHashMap中的并发修改

我是java.until.concurrent领域的新秀。 在代码中应用ConcurrentHashMap之前需要了解。 从理论上讲,地图内部可细分为16个同分异构体(尽管可以自定义默认细分)。 每个在处理时都适应不同的线程。 现在,有两个或多个线程试图操纵地图上的内容...那么,同步在
2回复

地图并发-修改异常

下面的代码由线程池执行。 首先,Map不是并发的,因此我已经对其进行了更改,但是第二行仍然有修改异常。 为了使这段代码具有线程安全性,我应该进行哪些更改? 一些更多信息:下面是返回地图的.load()函数的代码: 这是堆栈跟踪: 堆栈跟踪指向第二行(“ for”循环)
6回复

迭代器并发修改异常

如果在doSomething()中修改了列表,则此代码将抛出Concurrent Modification Exception。 是否可以通过将代码封装在某个同步块中来避免它?
2回复

Java Set中的并发修改异常

作为程序的一部分,我坚持并发修改异常。 这里是提到的部分: 这是calculateMarginalGain方法: 我在用**指定的程序行中遇到了此异常。 我发现此错误可能是由Iterator引起的。 但是我没有用过! 请帮助我找出导致该异常的原因以及如何解决该异常?
2回复

集合迭代器的并发修改异常

嗨,我有以下代码结构 但是现在,每个循环的调用函数都发生相同的异常。 这是为什么? 我不是在修改st集,而是在修改someset(类变量)。 请帮忙。
2回复

在同一arraylist上使用addall和removeall的并发修改异常

我必须从ArrayList删除一个子列表,并在开始时添加相同的子列表。 我正在使用以下代码- 我正在对addAll并发异常修改。 我尝试从q创建一个副本列表,然后在其上调用addAll ,但仍然在同一行上引发错误。 如何在同一数据列表上执行这两项操作?
2回复

解决swingworker应用程序中的并发修改异常

我正在编写一个应用程序,其中一些模拟在SwingWorker中运行。 在此模拟中,一些集保留了在SwingWorker的doInBackground()方法中修改的数据。 这些数据需要由GUI显示,但显然这会失败,因为一旦GUI开始访问集合,就会抛出并发修改异常,因为SwingWorker正
2回复

从并发修改的ConcurrentSkipListSet创建TreeSet的异常

通常,并发集合可以安全迭代; 根据Javadoc的说法:'迭代器是弱一致的,在迭代器创建时或之后的某个时刻返回反映集合状态的元素。 它们不会抛出ConcurrentModificationException,并且可能与其他操作同时进行。 但是,考虑一下: 输出是 我想知道这
3回复

如何通过CopyOnWriteArrayList / ConcurrentHashMap在内部处理并发修改异常?

我想在内部了解如何在ConcurrentHashMap和CopyOnWriteArrayList等并发集合中处理并发修改异常。 互联网上有很多博客,建议使用这两种数据结构来避免并发修改异常。 但没有解释,并发收集如何在内部处理此异常。 有人可以提供更多的见解吗? 我需要一些详细的