-
{@link ConcurrentModificationException}. Thus, in the face of
-
concurrent modification, the iterator fails quickly and cleanly, rather
-
than risking arbitrary, non-deterministic behavior at an undetermined
-
time in the future. The {@link Enumeration Enumerations} returned by
-
the {@link #elements() elements} method are not fail-fast.
-
Note that the fail-fast behavior of an iterator cannot be guaranteed
-
as it is, generally speaking, impossible to make any hard guarantees in the
-
presence of unsynchronized concurrent modification. Fail-fast iterators
-
throw {@code ConcurrentModificationException} on a best-effort basis.
-
Therefore, it would be wrong to write a program that depended on this
-
exception for its correctness: the fail-fast behavior of iterators
-
should be used only to detect bugs.
翻译
由iterator()和listIterator()返回的迭代器是fail-fast的。在于程序在对list进行迭代时,某个线程对该collection在结构上对其做了修改,这时迭代器就会抛出ConcurrentModificationException异常信息。因此,面对并发的修改,迭代器快速而干净利落地失败,而不是在不确定的情况下冒险。由elements()返回的Enumerations不是fail-fast的。需要注意的是,迭代器的fail-fast并不能得到保证,它不能够保证一定出现该错误。一般来说,fail-fast会尽最大努力抛出ConcurrentModificationException异常。因此,为提高此类操作的正确性而编写一个依赖于此异常的程序是错误的做法,正确做法是:ConcurrentModificationException 应该仅用于检测 bug。
大意为在遍历一个集合时,当集合结构被修改,很有可能会抛出Concurrent Modification Exception。为什么说是很有可能呢?从下文中我们可以知道,迭代器的remove操作(注意是迭代器的remove方法而不是集合的remove方法)修改集合结构就不会导致这个异常。
看到这里我们就明白了,fail-fast 机制是java容器(Collection和Map都存在fail-fast机制)中的一种错误机制。在遍历一个容器对象时,当容器结构被修改,很有可能会抛出ConcurrentModificationException,产生fail-fast。
再理解
大概意思是:在系统设计中,快速失效系统一种可以立即报告任何可能表明故障的情况的系统。快速失效系统通常设计用于停止正常操作,而不是试图继续可能存在缺陷的过程。这种设计通常会在操作中的多个点检查系统的状态,因此可以及早检测到任何故障。快速失败模块的职责是检测错误,然后让系统的下一个最高级别处理错误。
其实就是在做系统设计的时候先考虑异常情况,一旦发生异常,直接停止并上报,比如下面的这个简单的例子:
/**这里的代码是一个对两个整数做除法的方法,
*在fast_fail_method方法中,我们对被除数做了个简单的检查
*如果其值为0,那么就直接抛出一个异常,并明确提示异常原因。
*这其实就是fail-fast理念的实际应用。
*/
public int fast_fail_method(int arg1,int arg2){
if(arg2 == 0){
throw new RuntimeException(“can’t be zero”);
}
return arg1/arg2;
}
在Java集合类中很多地方都用到了该机制进行设计,一旦使用不当,触发fail-fast机制设计的代码,就会发生非预期情况。我们通常说的Java中的fail-fast机制,默认指的是Java集合的一种错误检测机制。当多个线程对部分集合进行结构上的改变的操作时,有可能会触发该机制时,之后就会抛出并发修改异常ConcurrentModificationException.当然如果不在多线程环境下,如果在foreach遍历的时候使用add/remove方法,也可能会抛出该异常。
在以下两种情况下会导致fail-fast,抛出ConcurrentModificationException
单线程环境
遍历一个集合过程中,集合结构被修改。注意,listIterator.remove()方法修改集合结构不会抛出这个异常。
多线程环境
当一个线程遍历集合过程中,而另一个线程对集合结构进行了修改。
单线程环境例子
import java.util.ListIterator;
import java.util.Vector;
public class Test {
/**
- 单线程测试
*/
@org.junit.Test
public void test() {
try {
// 测试迭代器的remove方法修改集合结构会不会触发checkForComodification异常
ItrRemoveTest();
System.out.println(“----分割线----”);
// 测试集合的remove方法修改集合结构会不会触发checkForComodification异常
ListRemoveTest();
} catch (Exception e) {
e.printStackTrace();
}
}
// 测试迭代器的remove方法修改集合结构会不会触发checkForComodification异常
private void ItrRemoveTest() {
Vector list = new Vector<>();
list.add(“1”);
list.add(“2”);
list.add(“3”);
ListIterator itr = list.listIterator();
while (itr.hasNext()) {
System.out.println(itr.next());
//迭代器的remove方法修改集合结构
itr.remove();
}
}
// 测试集合的remove方法修改集合结构会不会触发checkForComodification异常
private void ListRemoveTest() {
Vector list = new Vector<>();
list.add(“1”);
list.add(“2”);
list.add(“3”);
ListIterator itr = list.listIterator();
while (itr.hasNext()) {
System.out.println(itr.next());
//集合的remove方法修改集合结构
list.remove(“3”);
}
}
}
运行结果
1
2
3
----分割线----
1
java.util.ConcurrentModificationException
at java.util.Vector$Itr.checkForComodification(Unknown Source)
从结果中可以看到迭代器itr的remove操作并没有出现ConcurrentModificationException异常。而集合的remove操作则产生了异常。
多线程环境例子
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Vector;
public class Test {
private static List list = new Vector();
/**
- 多线程情况测试
*/
@org.junit.Test
public void test2() {
list.add(“1”);
list.add(“2”);
list.add(“3”);
// 同时启动两个线程对list进行操作!
new ErgodicThread().start();
new ModifyThread().start();
}
/**
- 遍历集合的线程
*/
private static class ErgodicThread extends Thread {
public void run() {
int i = 0;
while (i < 10) {
printAll();
i++;
}
}
}
/**
- 修改集合的线程
*/
private static class ModifyThread extends Thread {
public void run() {
list.add(String.valueOf(“5”));
}
}
/**
- 遍历集合
*/
private static void printAll() {
Iterator iter = list.iterator();
while (iter.hasNext()) {
System.out.print((String) iter.next() + ", ");
}
System.out.println();
}
}
运行结果
1, 2, 3,
1, 2, 3,
1, 2, 3,
1, Exception in thread “Thread-0” java.util.ConcurrentModificationException
at java.util.Vector$Itr.checkForComodification(Unknown Source)
从结果中可以看出当一个线程遍历集合,而另一个线程对这个集合的结构进行了修改,确实有可能触发ConcurrentModificationException异常。
下面是Vector中迭代器Itr的部分源码
/**
- An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator {
int expectedModCount = modCount;
//省略的部分代码
public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
synchronized (Vector.this) {
checkForComodification();
Vector.this.remove(lastRet);
expectedModCount = modCount;
}
cursor = lastRet;
lastRet = -1;
}
@Override
public void forEachRemaining(Consumer<? super E> action) {
//省略的部分代码
checkForComodification();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
且无助。**
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-zLzHTqZh-1715674642487)]
[外链图片转存中…(img-VaNIOgOX-1715674642488)]
[外链图片转存中…(img-PQNEsgWn-1715674642488)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!