2,结合底层代码讲述 fail-fast机制

内容提要
• 遍历ArrayList会遇到的坑:边遍历边修改可能出错
• 快速失效背后包含的保护机制

• 通过迭代器可以边遍历边修改

• 结合底层代码讲述fail-fast(快速失效)

• 引出快速失效话题的方法

遍历ArrayList时会遇到的坑
• 就遍历,没问题
• for,边遍历边修改没异常,但结果错

• foreach,边遍历边修改会抛异常
  for each抛异常的原因
• foreach是通过迭代器Iterator

2,结合底层代码讲述 fail-fast机制

实际上还是调的ArrayList的remove方法

2,结合底层代码讲述 fail-fast机制

2,结合底层代码讲述 fail-fast机制

 

走的是ArrayList的fastRemove方法

fastRemove方法里面有mountCount++

2,结合底层代码讲述 fail-fast机制

最后还会比较mountCunt和

2,结合底层代码讲述 fail-fast机制

结果导致 modCount 和 expectedModCount不一致所以导致异常的发生

异常如下:

2,结合底层代码讲述 fail-fast机制

 for each抛异常的原因?

• foreach是通过迭代器Iterator来访问的

• ArrayList的remove方法是会调用fastRemove

• fastRemove方法里modCount会加1

• 每次foreach,会调用checkForComodification方法

• 在其中modCount和expectedModCount不一致,抛异常

 

modCount是干啥的?

• ArrayList是线程不安全的,如一线程在读,另一线程在改,该抛异常

• 这就是fail-fast策略,该策略是通过modCount实现

• modCount是volatile,保证线程间修改可见性 


• 在foreach里边遍历边修改会抛异常,原因是,迭代用Iterator, remove用ArrayList的方法

• 迭代器初始化时,会将modCount值赋给expectedModCount。 在迭代中,始终判断 modCount 跟 expectedModCount 是否相 等,如果不相等就表示已经有其他线程修改,抛异常;

观察以for形式遍历的场景

• 不经过迭代器,直接访问ArrayList,所以不会引入fail-fast
• remove 2,预期输出1,2,3,4,实际输出1,2,4

2,结合底层代码讲述 fail-fast机制

 

观察以Iterator遍历和修改的场景

• 可边遍历边修改,且结果正确

2,结合底层代码讲述 fail-fast机制

 

改成这样就没问题啦

 

结合底层代码说明

 观察Iterator的remove方法,会重置expectedModCount • 重置的动机是:保证多个迭代器同时遍历时的数据一致

2,结合底层代码讲述 fail-fast机制

被人问的时候的说辞

 通过底层代码,展示边遍历边修改ArrayList的多种表 现,并阐述原因
• 这样能得到的评价不仅仅是:会用集合,而且更是:熟悉 JDK底层代码,熟悉Java核心基础
• 讲述底层代码设计的动机 • 扩展出volatile话题,同时引导进入集合并发话题
• 讲明尽量不边遍历边修改

引出fast-fail的方式
引出fast-fail的方式
• 介绍项目时,提一句,在实现xx模块时,我们用到了ArrayList,如 果有边遍历边修改的场景,我们需要考虑fail-fast因素
• 在简历里,找机会写一笔,熟悉JDK集合底层代码,包括fail-fast机制
• 回答好集合类问题时,再提一句,在使用集合对象时,我们需要 考虑fail-fast因素
• 在被问到多线程并发时,顺带提一句,在使用线程不安全的对象 时,比如ArrayList,我们需要考虑fail-fast机制