【问题标题】:How to simulate ConcurrentModificationException in own class?如何在自己的类中模拟 ConcurrentModificationException?
【发布时间】:2013-03-29 12:52:25
【问题描述】:

我一直在阅读《Effective Java》第 60 条,即“赞成使用标准异常”。

另一个值得了解的通用异常是 ConcurrentModificationException。如果为单线程或外部同步而设计的对象检测到它正在同时被修改。

当人们在循环时尝试从集合中删除时,通常会遇到CME

但是在这里我感兴趣的是检测对自我实现的类对象的并发修改的简洁示例?

我希望它类似于在内部对象和相关的布尔标志上同步,如果另一个线程遇到标志为假,则抛出异常。

对于我在ArrayList 的来源中找到的一个简单研究:

 final void checkForComodification() {
     if (modCount != expectedModCount)
         throw new ConcurrentModificationException();
 }

但是modCount 背后的原理是如何维护的。我找不到它被递减的地方。

【问题讨论】:

  • add 例如,你有这行:ensureCapacityInternal(size + 1); // Increments modCount!!
  • @assylias,我期待跟踪使用它的线程数。一旦一个线程停止使用它,那么 number 应该减少。但现在我知道它计算修改次数。

标签: java multithreading concurrency exception-handling concurrentmodification


【解决方案1】:

修改计数本质上是集合状态的"revision number"。每次对集合进行结构更改时,它都会增加。它永远不会递减。

开始迭代时,迭代器会记住 mod 计数的值。然后它会定期检查容器的当前 mod 计数是否仍然等于记住的值。如果没有——意味着自迭代开始以来结构发生了变化——抛出ConcurrentModificationException

【讨论】:

  • 感谢您解释这部分。那么,如果我想实现自己的对象并在两个线程尝试同时调用它的方法时抛出异常,这是通用方法吗?
  • @NikolayKuznetsov:我觉得这个方法很通用,可以用来检测各种不同情况下的变化。
【解决方案2】:

至于您将如何自己实现这种行为:您的类依赖于一些假设才能正常工作,如果对对象的访问未正确同步,则可能会违反这些假设。如果您发现它们不正确,请尝试检查这些假设并抛出 CME。

ArrayList 的示例中,假设没有人会在您对其进行迭代时更改列表的结构,因此ArrayList 会跟踪在迭代期间不应更改的修改计数。

然而,这种检查只是为了更有可能错误的访问将导致一个干净的异常而不是奇怪的行为 - 换句话说,这个异常只是对开发人员的帮助,并且确实不需要强制正确性,因为当你遇到正确性时就已经妥协了。

提供这种帮助是个好主意,它不会对您的班级表现产生太大影响,但使用例如同步以确保正确使用可能是个坏主意 - 那么您不妨一开始就让类线程安全。

这就是为什么ArrayList 的 API 文档说:

请注意,无法保证迭代器的快速失败行为 因为一般来说,不可能做出任何硬性保证 在存在不同步的并发修改的情况下。快速失败 迭代器尽最大努力抛出 ConcurrentModificationException 基础。因此,编写一个依赖的程序是错误的 关于这个例外的正确性:快速失败的行为 迭代器应该只用于检测错误。

【讨论】:

    【解决方案3】:

    所以,一般的做法是:记住对象的当前状态,每次尝试访问对象时检查它的状态,如果状态改变,抛出你的异常!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-22
      • 2013-02-17
      • 2021-03-31
      • 2015-04-15
      • 1970-01-01
      • 2012-11-15
      • 2015-11-06
      • 1970-01-01
      相关资源
      最近更新 更多