volatile
volatile的本意是“易变的”。volatile关键字修饰符是一种类型编译器,用它声明的类型变量表示可以被某些未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。volatile 指出 i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取。对于volatile类型的变量,系统每次用到他的时候都是直接从对应的内存当中提取,而不会利用cache当中的原有数值,以保证每次取出的值都是最新的。
fail-fast
fail-fast 机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。但java JDK并不保证fail-fast一定会发生。
fail-fast实现
1 | /** |
从上面的注释中可以知道,modCount的值会在底层数组内容被修改的时候(如进行add, remove, clear等操作的时候)被修改。
在每次新建Iterator对象的时候,都会用上面的modCount初始化Iterator中的expectedModCount值。并在进行next、remove等操作的时候利用checkForComodification()对 expectedModCount 和 modCounts 是否相等进行检查。如果不相等,则抛出ConcurrentModificationException 异常。
fail-fast解决办法
由于java JDK不保证fail-fast机制一定会发生,因此在多线程情况下解决fail-fast的办法一般有两种:
使用java.util.concurrent包下的类去替代java.util包下的类
加锁
疑问
1 | ArrayList<Integer> list = new ArrayList<>(); |
其实不用在多线程的情况下,上面的代码就可以抛出ConcurrentModificationException。(这种情况下ConcurrentModificationException这个命名是不是有点儿名不符实呐)
在我看来,fail-fast更像是一个强有力的warning。强制地提示你,你的代码里存在隐患。由于多线程的问题不容易被定位,所以编译器强制你使用更加安全的方式来实现你的逻辑。
就像上面的代码,我其实已经知道我正在使用iterator的低层数组被某些方法改变,我只是不想再new一个新的出来,但是仍然会抛出异常来强制你使用更安全的方式来进行操作。