是否在ConcurrentModificationException之后保证Java集合处于有效的可用状态?

Hal*_*own 8 java collections multithreading

我正在使用即时模式GUI模式编写GUI应用程序,并且UI在独立于引擎的线程上运行,该引擎为应用程序的实际功能提供了动力。GUI线程最终会遍历引擎线程在概念上“拥有”的许多对象列表,并且这些列表很少发生更改。GUI线程是vsync的,这意味着它以大约60Hz的频率运行,而引擎线程以大约200Hz的频率运行。

有时,UI中的操作会更改引擎中集合的内容,并且我有一个消息传递系统将Runnables发布到引擎线程中以执行这些突变,以确保这些突变不会与引擎中发生的冲突发动机。这样,我可以确保引擎始终看到一致的数据视图,这对于我的应用程序非常重要。

但是,由于引擎负责所有数据突变,因此有时会发生这样的情况:引擎在迭代GUI时更改了集合的内容,并且由于这些集合是标准Java集合,因此可以预测并正确地抛出ConcurrentModificationException。我可以想到一些高级方法来解决此问题:

  1. 通过使用同步集合或读写锁来锁定
  2. 对GUI线程读取的数据进行双缓冲,并在完成绘制框架后让GUI线程翻转双缓冲
  3. 忽略CME并中止绘制其余帧,这将为发生“不良”突变的帧绘制部分信息,然后继续进行下一帧

锁定会带来很大的性能损失,虽然GUI有时会在等待从引擎线程获取锁定时停滞是可以的,但对于引擎线程而言,以一致的速度运行非常重要,甚至R / W锁定会导致发动机螺纹失速。双缓冲具有显着的复杂性,因为GUI在每个帧上读取大量数据。

我为您提供所有这些背景知识,因为我知道选项3很难看,而且我的问题在某种意义上是“不合适的问题”。Javadoc ConcurrentModificationException甚至说:

请注意,不能保证快速故障行为,因为通常来说,在存在不同步的并发修改的情况下,不可能做出任何严格的保证。快速失败操作会尽最大努力抛出ConcurrentModificationException。因此,编写依赖于此异常的程序的正确性是错误的:ConcurrentModificationException应该仅用于检测错误。

但!对于CME可能损坏的单个框架的GUI的正确性,我并不担心。我只关心下一帧会发生什么。这就引出了我的问题:从迭代器中抛出a之后,继续使用Java集合(我对ArrayListand 的答案最感兴趣HashMapConcurrentModificationException是否安全?这样做似乎是合乎逻辑的,但是我找不到找到说明在抛出CME之后对象仍将处于可用状态的文档。显然,迭代器在那时是敬酒的,但是我想吞下异常并继续使用该集合。

And*_*eas 7

引用部分报价:

因此,这将是错误的编写一个程序,依赖于此异常的它的正确性:ConcurrentModificationException的应该仅用于检测bug

结论:您的代码有错误,需要修复,因此集合处于什么状态都没有关系。

永远不会在有效代码中收到该错误。这是一个绝对不应该被抓住并采取行动的例外。

  • @HaldeanBrown我的意思是,您永远不要围绕“ ConcurrentModificationException”进行编码。例外情况是非保证的快速失败,它只是告诉程序员(您是程序员)您编写了错误的代码,并且代码需要修复。收到该错误时,唯一正确的措施是“修复”代码,而不是添加hacky代码来解决该问题。您甚至正在考虑这样做的事实意味着您错过了报价中突出显示的部分,这就是该答案试图教给您的内容。 (3认同)
  • @markspace实际上,[`ConcurrentModificationException`](https://docs.oracle.com/javase/8/docs/api/java/util/ConcurrentModificationException.html)仅表示在对集合进行迭代时直接对其进行了修改。 。它绝非关于形状修改是否正确进行的信息。它只是说**`Iterator` **不再有效。不是线程安全的集合的多线程更新不会导致该异常,它只会静默破坏该集合。无论异常名称如何,该异常都与线程安全性无关。 (3认同)

Pet*_*rey 6

对于大多数馆藏,只要您只有一位作家,您也许还可以,但是并不能保证。如果您在HashMap上有多个编写器,那么您可能会得到一个损坏的地图,该地图的结构具有无限循环。

如果您只想获得一个锁(如果不使用ReentrantLock它的tryLock()话),则建议您使用一种有方法的方法来最大程度地减少阻塞。

将数据从一个线程传送到另一个线程的另一种方法是Queue修改任务,引擎在不繁忙时可以接管。这仅允许通过一个线程进行所有访问。

顺便说一句:锁定/解锁大约需要1微秒,除非您经常这样做,否则对您的影响不大。

  • 有多个作者不会导致`ConcurrentModificationException`。尽管这个名称听起来像多线程异常,但事实并非如此。单线程代码也可能导致异常。---例如,在HashMap上有多个作者,这可能会损坏地图,除非您也要对其进行迭代,否则不会导致异常,并且绝不会对许多地图进行迭代。---我相信您的答案缺少`ConcurrentModificationException`的要点。 (3认同)
  • @Andreas我试图解决OP设计问题,而不是解释ConcurrentModificationException的含义,尽管您是正确的并且有观点,但我认为这对OP没有帮助。 (2认同)