什么是Fail-Fast
- 在多线程环境下,线程A在进行遍历时,如果线程B修改了集合中的内容,线程A在遍历时会抛出并发修改异常
- 同时也出现在单线程环境中,线程在遍历的过程中同时对集合中的内容进行了修改,此时也会抛出并发修改异常
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
String tmp = iter.next();
System.out.println(tmp);
if (tmp.equals("1")) {
list.remove("1");
}
}
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
String tmp = iter.next();
System.out.println(tmp);
if (tmp.equals("2")) {
list.remove("2");
}
}
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
String tmp = iter.next();
System.out.println(tmp);
if (tmp.equals("2")) {
list.remove("2");
}
}
List<String> list = Arrays.asList("1", "2", "3", "4");
for (String i : list) {
if ("1".equals(i)) {
list.remove("1");
}
}
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
String tmp = iter.next();
System.out.println(tmp);
if (tmp.equals("1")) {
iter.remove("1");
}
}
- 在代码块13中,会抛出并发修改异常
**ConcurrentModificationException**
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)
at java.util.ArrayList$Itr.next(ArrayList.java:861)
at com.example.homework.controller.Client.main(Client.java:23)
- 代码块4中,会抛出异常** **
**UnsupportedOperationException**
为什么代码块13抛出异常
**ArrayList**
中的使用的迭代器的**Next**
方法在内部类Itr中- 在迭代器
**next**
方法中,会调用**checkForComodification**
- 在
**modCount != expectedModCount**
时,就会抛出**ConcurrentModificationException**
**modCount**
记录的是**操作次数**
,在**ArrayList**
方法中,**add**
和**remove**
方法会使**modCount+1**
,set方法不会操作modCount- 因为
**expectedModCount**
是在**创建迭代器时**
初始化赋值,使用**ArrayList**
的**remove**
方法不会修改**expectedModCount**
,因此在代码块13中会因为**modCount**
和**expectedModCount**
不相等而抛出异常
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
为什么代码块2不抛出异常
- 在迭代器循环中会使用
**hasNext**
方法判断是否含有下一个元素,判断条件是**cursor != size **
- 在代码块2中在调用
**ArrayList.remove**
方法后,删除了该元素,此时集合会size-1 **cursor**
属性代表中**next方法要访问的元素的下标**
,代码块2中此时遍历到**倒数第二个元素**
,因为此时size-1后,此时**cursor和size相等**
,迭代器会判断已经遍历到集合的末尾,从而退出循环。- 此时不会访问到集合末尾的元素,也就没
**有再调用末尾的next**
方法,因此也就**不会抛出对应的异常**
。
为什么代码块4抛出异常
- 代码块4中的集合不是java.util包下的ArrayList,这个是Arrays工具类中一个
**内部类ArrayList**
- 他的元素集合定义为
**final**
,并且该类**没有重写**
add、remove等方法,在 AbstractList 的默认实现中会抛出对应的异常
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
为什么代码块5不会抛出异常
- 代码块5使用的是迭代器进行元素的删除
- 在迭代器的元素删除中,会先调用
**ArrayList**
的**remove**
方法,然后再将新的**modCount**
值赋值给**expectedModCount**
- 因为迭代器中的 可以
**实时更新**
,因此不会因为**modCount**
和**expectedModCount**
不相等而抛出异常
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
Fail-safe
copyOnWriteArrayList
没有设计抛出并发修改异常的代码