Java fail-fast 与 fail-safe 机制对比
关于fail-fast参考这篇文章:
从 modcount 看 java集合 fail-fast 机制
一、fail-safe概述以及与fail-fast区别
首先 fail-safe 并不属于javase规范术语,只是用以说明 fail-fast 与 non-fail fast 的区别。
这种机制的迭代器允许在迭代时修改集合,且不会抛出任何异常。不同的类实现方式有所不同,通常是因为"迭代"与"修改操作"使用的不是同一个数据数组,比如copyonwirtearraylist在修改时会创建一个原数组副本(只是新建一个数组,浅克隆)在新数组上修改后再将指针指向新数组,而此时迭代器中的指针仍指向原数组。
因此这种迭代器无法保证在迭代时获取的是最新数据,比如:(1)copyonwritearraylist
在迭代过程中的数据更新无法在迭代中表现出来。(2)concurrenthashmap
的弱一致性迭代器。
注:弱一致性迭代器可能会(但不保证)将迭代过程中的修改表现出来。
concurrenthashmap迭代器官方注释:
the iterators returned by concurrenthashmap is weakly consistent. this means that this iterator can tolerate concurrent modification, traverses elements as they existed when iterator was constructed and may (but not guaranteed to) reflect modifications to the collection after the construction of the iterator.
二、示例
2.1 copyonwritearraylist示例
public static void main(string args[]) { copyonwritearraylist<integer> list = new copyonwritearraylist<integer>(new integer[] { 1, 3, 5, 8 }); iterator itr = list.iterator(); while (itr.hasnext()) { integer no = (integer)itr.next(); system.out.println(no); if (no == 8) list.add(14); //不会打印 } }
输出:
1 3 5 8
2.2 concurrenthashmap示例
public static void main(string[] args) { // creating a concurrenthashmap concurrenthashmap<string, integer> map = new concurrenthashmap<string, integer>(); map.put("one", 1); map.put("two", 2); map.put("three", 3); map.put("four", 4); // getting an iterator from map iterator it = map.keyset().iterator(); while (it.hasnext()) { string key = (string)it.next(); system.out.println(key + " : " + map.get(key)); //一般会打印 //但若改成("five",5)则基本不会打印,可能与弱一致性迭代器内部逻辑有关 map.put("seven", 7); } }
三、实现原理解析
3.1 copyonwritearraylist对fail-safe实现
copyonwritearraylist 在修改时会创建一个原数组副本(只是新建一个数组,浅克隆)在新数组上修改后再将集合的数组指针指向新数组对象,而此时迭代器中的指针仍指向原数组对象。迭代过程中的修改,不会反映到迭代上。
源码解析:
public class copyonwritearraylist<e> implements list<e>, randomaccess, cloneable, java.io.serializable { //!只放关键代码 /** the array, accessed only via getarray/setarray. */ private transient volatile object[] array; //存放数据数组 //添加方法 public boolean add(e e) { final reentrantlock lock = this.lock; lock.lock(); try { object[] elements = getarray(); int len = elements.length; object[] newelements = arrays.copyof(elements, len + 1); //浅克隆原数组并长度+1 newelements[len] = e; //在新数组上进行添加 setarray(newelements); //将数组指针指向新数组 return true; } finally { lock.unlock(); } } //迭代器方法 public iterator<e> iterator() { return new cowiterator<e>(getarray(), 0); //直接用原来的数据数组 } final object[] getarray() { return array; } static final class cowiterator<e> implements listiterator<e> { /** snapshot of the array */ private final object[] snapshot; /** index of element to be returned by subsequent call to next. */ private int cursor; //始终指向下一个元素 private cowiterator(object[] elements, int initialcursor) { cursor = initialcursor; snapshot = elements; //将引用snapshot指向传入的原数组 } //… public e next() { if (! hasnext()) throw new nosuchelementexception(); return (e) snapshot[cursor++]; } } }
3.2 concurrenthashmap对fail-safe实现
concurrenthashmap并非通过拷贝数组修改来实现的fail-safe…
//todo:
参考: