主要思想
- 快速失败机制是java集合的一种错误检测机制,当迭代集合时集合的结构发生改变,就会产生fail-fast机制。
- 一旦发现遍历的同时,其他人来修改,就立刻抛出异常。
fail_salf:当遍历的时候,其他人来修改,应当有相应的策略,例如牺牲一致性来遍历整个数组。
单线程情况
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for(int i = 0; i < 10; i++){
list.add(i+"");
}
Iterator<String> iterator = list.iterator();
int i = 0;
while (iterator.hasNext()){
if(i==3){
list.remove(3);
}
System.out.println(iterator.next());
i++;
}
}
}
迭代器遍历集合时,将其中的一个元素删除,就会发生fail-fast。
多线程情况
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class FailFastTest {
public static List<String> list = new ArrayList<>();
private static class My2 extends Thread{
int i = 0;
@Override
public void run() {
while (i<5){
System.out.println("thread2:"+i);
if(i==2){
list.remove(i);
}
try {
Thread.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
}
i++;
}
}
}
private static class My1 extends Thread{
@Override
public void run() {
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String s = iterator.next();
System.out.println(this.getName()+":"+s);
try {
Thread.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
}
}
super.run();
}
}
public static void main(String[] args) {
for(int i = 0; i < 10; i++){
list.add(i+"");
}
My1 myThread1 = new My1();
My2 myThread2 = new My2();
myThread1.setName("thread1");
myThread2.setName("thread2");
myThread1.start();
myThread2.start();
}
}
线程1对集合遍历,线程2删除集合元素。
下面是Iterator实现类的源码:
/**
* Index of element to be returned by subsequent call to next.
*/
int cursor = 0;
/**
* Index of element returned by most recent call to next or
* previous. Reset to -1 if this element is deleted by a call
* to remove.
*/
int lastRet = -1;
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
int expectedModCount = modCount;
每次对集合进行修改时,都会改变modCount的值。而expectedModCount的值是modCount的最初值。也就是会检测modCount是否等于expectedModCount的值,相等就遍历,不相等就抛出异常。
1.在遍历过程中,所有涉及到改变modCount值的地方全部加上synchronized。
2.使用CopyOnWriteArrayList来替换ArrayList。