
本文共 3160 字,大约阅读时间需要 10 分钟。
fail-fast机制与ConcurrentModificationException
在软件开发中,fail-fast机制是一种强大的异常处理方式,能够及时发现潜在的系统故障。这种机制通过在异常发生时立即停止正常操作并报告错误,帮助开发者快速定位问题。然而,在Java的集合类应用中,fail-fast机制可能会引发ConcurrentModificationException(以下简称CME),这通常让程序员感到困惑,特别是在明明没有使用多线程的情况下。接下来,我们将深入探讨这个问题的本质及其解决方案。
fail-fast机制的定义
fail-fast机制的核心思想是在系统设计中优先考虑异常情况。一旦检测到潜在的错误或异常,系统会立即停止正常操作,并通过异常报告错误。这种机制通常用于防止程序在执行过程中因潜在错误而继续运行,从而避免数据不一致或其他严重后果。
一个简单的例子:
public int divide(int divisor, int dividend) { if (dividend == 0) { throw new RuntimeException("dividend can't be null"); } return divisor / dividend;}
在上面的代码中,方法首先检查被除数是否为零,如果是,则立即抛出异常。这就是典型的fail-fast机制的应用。这种做法的好处是可以提前识别一些错误,并对这些错误进行针对性处理。
Java集合类中的fail-fast机制
在Java中,fail-fast机制尤其常见于集合类的使用。当多个线程试图对同一个集合进行结构修改时,集合会检测到这种并发修改,并通过抛出ConcurrentModificationException来警告程序员。这种机制默认是启用的。
CME的触发条件
CME通常在以下情况下发生:
remove(Object o)
、add(Object o)
等方法进行操作时,如果在这个操作之前集合已经被其他线程修改过,同样会抛出CME。CME的复现
我们可以通过以下代码来复现CME:
ListuserNames = new ArrayList<>();userNames.add("Hollis");userNames.add("hollis");userNames.add("HollisChuang");userNames.add("H");for (String userName : userNames) { if (userName.equals("Hollis")) { userNames.remove(userName); }}System.out.println(userNames);
在上述代码中,foreach循环遍历集合并尝试删除"Hollis"
元素。运行该代码会抛出CME,提示并发修改。
CME的原理
CME的触发源于集合的modCount
和expectedModCount
两个变量的不一致。modCount
记录集合的修改次数,而expectedModCount
是迭代器在创建时预期的修改次数。当迭代器检测到modCount
与expectedModCount
不一致时,就会抛出CME。
modCount与expectedModCount的作用
- modCount:每当对集合进行修改操作(如add、remove、clear等)时,
modCount
会被递增。 - expectedModCount:当创建一个迭代器时,
expectedModCount
会被设置为当前的modCount
。迭代器在每次调用next()
时会检查modCount
是否等于expectedModCount
。
如果modCount
与expectedModCount
不一致,迭代器会抛出CME。
CME的触发条件
当在增强for循环中对集合进行修改操作时,集合会检测到并发修改,并通过抛出CME来警告程序员。这种机制的目的是防止在集合结构发生变化的情况下继续进行迭代,避免潜在的逻辑错误。
如何解决CME问题
为了避免CME带来的不便,可以使用一些特殊的集合类,这些集合类支持在foreach循环中进行元素的添加或删除操作,而不会抛出CME。这些集合类被称为fail-safe集合。
CopyOnWriteArrayList
CopyOnWriteArrayList
是一个典型的fail-safe集合类。它的工作原理是基于写时复制的策略,确保在并发环境下可以安全地进行读写操作。
CopyOnWriteArrayList的特点
CopyOnWriteArrayList的实现原理
CopyOnWriteArrayList
在写操作时会创建一个新的数组副本,然后在副本上进行操作。这样,在读操作时,直接访问副本中的数据,不会受到原集合的修改影响。
CopyOnWriteArrayList的使用示例
CopyOnWriteArrayListuserNames = new CopyOnWriteArrayList<>();userNames.add("Hollis");userNames.add("hollis");userNames.add("HollisChuang");userNames.add("H");for (String userName : userNames) { if (userName.equals("Hollis")) { userNames.remove(userName); }}System.out.println(userNames);
在上述代码中,使用CopyOnWriteArrayList
进行增强for循环中的元素删除操作,运行时不会抛出CME。CopyOnWriteArrayList
通过复制原集合,确保在读操作时不会受到并发修改的影响。
Copy-On-Write机制
Copy-On-Write(COW)是一种优化策略,通过延迟修改来减少对内存的占用。其核心思想是从共享的数据结构中延迟地创建新的副本,并在副本上进行修改,而不是直接修改原始数据结构。
COW的优点
COW的缺点
总结
fail-fast机制是一种强大的异常处理机制,能够及时发现系统的潜在错误。然而,在Java的集合类中,fail-fast机制可能会在增强for循环中进行元素修改时抛出ConcurrentModificationException。为了避免这种情况,可以使用CopyOnWriteArrayList
等fail-safe集合类,它们支持在foreach循环中进行元素的添加或删除操作,而不会抛出CME。
发表评论
最新留言
关于作者
