【面试:基础篇06.2:FailFast与FailSafe源码分析】
FailFast源码分析
ArrayList<Integer> L=new ArrayList<>();
for (int i=0;i<5;i++) {
L.add(i);
}
Iterator<Integer> it = L.iterator();
while (it.hasNext()){
int val = it.next();
L.add(5);// 在迭代器遍历过程中出现报错 ConcurrentModificationException 并发修改异常
System.out.println(val);
}
我们进入ArrayList()的迭代器Iterator it = L.iterator();
我们应该关注两个变量 ==expectedModCount==与==modCount==
modCount记录ArrayList集合修改了多少次,这个例子中因为add添加了5次 所以modCount为5expectedModCount是迭代器成员变量,记录了刚开始ArrayList修改了多少次,初始化expectedModCount=modCount=5
接下来我们应该关注 每次执行add方法会 先执行==checkForComodification()==方法
我们可以看出checkForComodification()方法是判断expectedModCount与modCount是否相等,如果不等 说明ArrayList集合被修改了 因为我们在迭代过程中又add了一个元素 所以此时modCount=6 expectedModCount=5,expectedModCount!=modCount 所以 throw new ConcurrentModificationException(); 就是我们看到的抛出 并发异常。乐观锁的思想:即并发情况下 乐观的认为所有的操作都不会修改数据 所以不会上锁 只有遇到了并发操作的情况下 才会报错 抛出异常。
FailSafe源码分析
CopyOnWriteArrayList<Integer> L = new CopyOnWriteArrayList<>();
for (int i=0;i<5;i++) {
L.add(i);
}
Iterator<Integer> it = L.iterator();
while (it.hasNext()) {
int val = it.next();
L.add(9);
System.out.printf("%d ",val);
}
System.out.println();
System.out.println(L);
我们进入CopyOnWriteArrayList()的迭代器Iterator it = L.iterator();
我们注意到了 ==COWIterator==查看它
我们应该关注==snapshot = elements==这个语句
elements当前正在遍历的数组,snapshot记录当前正在遍历的数组,也就是说在迭代器中 遍历的是snapshot记录的数组
我们再来看看CopyOnWriteArrayList里的add方法
我们应该关注==getArray();==, ==Arrays.copyOf(elements, len + 1);==,==newElements[len] = e;==,==setArray(newElements);==
getArray();拿到原来旧的数组Arrays.copyOf(elements, len + 1); 拷贝原数组 并且长度+1
newElements[len] = e;表示把新add的元素放入新数组中
setArray(newElements);说明现在newElements是CopyOnWriteArrayList的新数组,也就说明我们迭代器结束后遍历的其实是newElements而不是迭代器中记录的数组,所以这才使得,在迭代器遍历过程中 遍历的是最开始的数组,迭代器结束后遍历的是新数组
CopyOnWriteArrayList是FailSafe的典型代表,CopyOnWriteArrayList属于读写分离 也是边读边拷贝