本文源码基于 jdk8
fast-fail 机制s分析
什么是 fast-fail ?
让我们用代码的形式来理解很清晰。
- 运行这段代码
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(1);list.add(2);list.add(4);
ListIterator iterator = list.listIterator();
iterator.add(4);
System.out.println(list);
//list.add(3);
while (iterator.hasNext()){//迭代器的迭代方法
System.out.println(iterator.next());
}
}
- 控制台输出
[1, 2, 4]
1
2
4
- 去掉注释后 ~~//~~list.add(3);
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:966)
at java.util.LinkedList$ListItr.next(LinkedList.java:888)
at Test.main(Test.java:14)
okay,看完样例后,我们就清楚这个 fast-fail 是啥意思了,那么这是为什么呢,跟我一起走进源码看看。
这是检查是否更改方法所抛出的异常,往上翻发现listItr中其他的方法基本都调用了这个方法.
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
modCount是从父类AbstractList中继承的,查看源码注释
The number of times this list has been structurally modified. Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.
modCount记录了导致结构变化的操作次数
- 为什么不能保证fail-fast呢?
因为若是并发操作结构变化后modcount仍未修改,然而此时迭代器已经读入modecount,那么就不会抛出异常,也就是说这个机制就失效了。
样例:
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(1);list.add(2);list.add(4);
Iterator iterator = list.iterator();
System.out.println(list);
new Thread(()->{//添加新线程执行add
list.add(4);
}).start();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
控制台输出:
[1, 2, 4]
1
2
4
Iterator和foreach循环有什么关系?
//代码
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(1);list.add(2);list.add(4);
for (Integer integer : list) {
System.out.println(integer);
}
}
IDEA(安装插件后)查看字节码**【foreach循环片段】**
34 pop
35 aload_1
36 invokevirtual #6 <java/util/LinkedList.iterator>
39 astore_2
40 aload_2
41 invokeinterface #7 <java/util/Iterator.hasNext> count 1
46 ifeq 69 (+23)
49 aload_2
50 invokeinterface #8 <java/util/Iterator.next> count 1
55 checkcast #9 <java/lang/Integer>
58 astore_3
59 getstatic #10 <java/lang/System.out>
62 aload_3
63 invokevirtual #11 <java/io/PrintStream.println>
66 goto 40 (-26)
69 return
结论:foreach循环实际上是调用(动态链接?//TODO)迭代器的迭代方法,所以foreach过程中不允许其他修改实例的操作。(参考fast-fail)
forEach又是什么鬼??
foreach(加强循环)是一个语法结构,实际是用迭代器(Iterator)的迭代方法。
forEach是Iterable接口的default方法(以实现方法)
Iterable是所有拥有Iterator需要实现的接口
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
okay,大家一看到这个循环就懂了,不过是包装了消费者模式的皮而已_
写段代码来展示一下它的威力吧
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(1);list.add(2);list.add(4);
list.forEach((str)->{//函数式匿名类
System.out.println(str);
});
}
//控制台输出
1
2
4
看看它的部分字节码?(用idea来查看bytecode可以展示出模式的作用)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Y3nom8I-1587944944858)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200421181509466.png)]
结论:forEach 仍然是使用迭代器的迭代方法,那么当然也不能做修改实例结构的操作。