fail-safe 和 fail-fast 都是什么鬼?
點(diǎn)擊關(guān)注公眾號,Java干貨及時(shí)送達(dá)
你真的了解 fail-fast 和 fail-safe 嗎???
簡介
java.util 包下的 屬于 fail-fast , 快速失敗~ ?? java.util.concurrent 包下的 屬于 fail-safe ,安全失敗~ ??fail-fast 在迭代時(shí),如果發(fā)現(xiàn) 該集合數(shù)據(jù) 結(jié)構(gòu)被改變 (modCount != expectedModCount),就會(huì) 拋出 ConcurrentModificationException fail-fast 實(shí)驗(yàn)代碼
Hashtable,這里采用 jdk1.7 的寫法 ~ ConcurrentHashMap 在7和8中有啥不一樣 ??class E implements Runnable{
Hashtable<String, String> hashtable;
public E(Hashtable<String, String> hashtable) {
this.hashtable = hashtable;
}
private void add(Hashtable<String, String> hashtable){
for (int i = 0; i < 10000000; i++) {
hashtable.put("a",""+i);
}
}
@Override
public void run() {
add(hashtable);
}
}
public class D {
public static void main(String[] args) {
Hashtable<String, String> hashtable = new Hashtable<String, String>();
hashtable.put("1","2");
hashtable.put("2","2");
hashtable.put("3","2");
hashtable.put("4","2");
hashtable.put("15","2");
new Thread(new E(hashtable)).start();
Set<Map.Entry<String, String>> entries = hashtable.entrySet();
Iterator<Map.Entry<String, String>> iterator = entries.iterator();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (iterator.hasNext()){
System.out.println(iterator.next());
iterator.remove();
}
}
}

觸發(fā)的原理:

結(jié)論:
HashTable 是 線程安全的 , 但是它有 fail-fast 機(jī)制 ,所以在多線程情況下進(jìn)行 迭代 也不能去修改它的數(shù)據(jù)結(jié)構(gòu)!fail-fast 機(jī)制 不允許并發(fā)修改! fail-safe 實(shí)驗(yàn)代碼
class E implements Runnable{
ConcurrentHashMap<String, String> concurrentHashMap;
public E(ConcurrentHashMap<String, String> concurrentHashMap) {
this.concurrentHashMap = concurrentHashMap;
}
private void add( ConcurrentHashMap<String, String> concurrentHashMap){
for (int i = 0; i < 100000; i++) {
concurrentHashMap.put("a"+i,""+i);
}
}
@Override
public void run() {
add(concurrentHashMap);
}
}
public class D {
public static void main(String[] args) {
ConcurrentHashMap<String, String> concurrentHashMap = new ConcurrentHashMap<String, String>();
concurrentHashMap.put("1","2");
concurrentHashMap.put("2","2");
concurrentHashMap.put("3","2");
concurrentHashMap.put("4","2");
concurrentHashMap.put("15","2");
new Thread(new E(concurrentHashMap)).start();
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
Set<Map.Entry<String, String>> entries = concurrentHashMap.entrySet();
for (Map.Entry<String, String> entry : entries) {
System.out.println(entry);
// 這里不用調(diào)用 iterator 去 remove
concurrentHashMap.remove(entry.getKey());
}
}
}

ConcurrentModificationException ,但是無法保證拿到的是最新的值
為什么可以調(diào)用它自身的 remove 呢?





EntryIterator, 而 它又 繼承了 HashIterator ,在初始化時(shí),會(huì)先調(diào)用父類的構(gòu)造器。HashIterator 在初始化 時(shí),會(huì)去調(diào)用 advance 方法 (這里就不展開這個(gè) concurrentHashMap結(jié)構(gòu)啦~ ) 這里的重點(diǎn)在最后一張圖 , 它調(diào)用的是 UNSAFE.getObjectVolatile 。HashMap 或者 上面的 HashTable,他們都是直接 拿到代碼中定義的這個(gè) Entry[]~。??
fail-fast 和 fail-safe 的區(qū)別時(shí),看到下面這張圖。fail-safe 會(huì)復(fù)制原來的集合,然后在復(fù)制出來的集合上進(jìn)行操作,然后就說這樣是不會(huì)拋出 ConcurrentModificationException 異常了。CopyOnWriteArrayList 或者 CopyOnWriteArraySet 的情況(下面的源碼講到~)
CopyOnWriteArrayList 源碼snapshot 的指針是始終指向這個(gè)原數(shù)組的(當(dāng)你創(chuàng)建迭代器的時(shí)候)

結(jié)論
fail-safe 也是得具體情況具體分析的。如果是
CopyOnWriteArrayList或者CopyOnWriteArraySet,就屬于 復(fù)制原來的集合,然后在復(fù)制出來的集合上進(jìn)行操作 的情況 ,所以是不會(huì)拋出這個(gè)ConcurrentModificationException的 。如果是這個(gè)
concurrentHashMap的,就比較硬核了~ ?? 它直接操作底層,調(diào)用UNSAFE.getObjectVolatile,直接 強(qiáng)制從主存中獲取屬性值,也是不會(huì)拋出這個(gè)ConcurrentModificationException的 。并發(fā)下,無法保證 遍歷時(shí)拿到的是最新的值~
remove 的問題啦~remove 的源碼如下


setEntryAt ,這里也是出現(xiàn)了這個(gè) UNSAFE ??UNSAFE.putOrderedObject 這段代碼的意思就是 :總結(jié)
java.util 包下的屬于fail-fast ,ConcurrentModificationException ,modCount != expectedModCount 。??java.util.concurrent 包下的 屬于 fail-safe ,concurrentHashMap 獲取和修改數(shù)據(jù)時(shí) ,是通過 UNSAFE 類 直接從主內(nèi)存中獲取或者更新數(shù)據(jù)到主內(nèi)存~ 
CopyOnWriteArrayList 或者 CopyOnWriteArraySet ,就直接 復(fù)制原來的集合,然后在復(fù)制出來的集合上進(jìn)行操作 。






關(guān)注Java技術(shù)??锤喔韶?/strong>

評論
圖片
表情

