fail safe java_Java中快速失败(fail-fast)和安全失败(fail-safe)的区别?
快速失敗(fail—fast):
在用迭代器遍歷一個集合對象時,如果遍歷過程中對集合對象的內容進行了修改(增加、刪除、修改),則會拋出Concurrent Modification Exception。
原理:迭代器在遍歷時直接訪問集合中的內容,并且在遍歷過程中使用一個 modCount 變量。集合在被遍歷期間如果內容發生變化,就會改變modCount的值。每當迭代器使用hashNext()/next()遍歷下一個元素之前,都會檢測modCount變量是否為expectedmodCount值,是的話就返回遍歷;否則拋出異常,終止遍歷。
注意:這里異常的拋出條件是檢測到 modCount!=expectedmodCount 這個條件。如果集合發生變化時修改modCount值剛好又設置為了expectedmodCount值,則異常不會拋出。因此,不能依賴于這個異常是否拋出而進行并發操作的編程,這個異常只建議用于檢測并發修改的bug。
場景:java.util包下的集合類都是快速失敗的,不能在多線程下發生并發修改(迭代過程中被修改)。
安全失敗(fail—safe):
采用安全失敗機制的集合容器,在遍歷時不是直接在集合內容上訪問的,而是先復制原有集合內容,在拷貝的集合上進行遍歷。
原理:由于迭代時是對原集合的拷貝進行遍歷,所以在遍歷過程中對原集合所作的修改并不能被迭代器檢測到,所以不會觸發Concurrent Modification Exception。
缺點:基于拷貝內容的優點是避免了Concurrent Modification Exception,但同樣地,迭代器并不能訪問到修改后的內容,即:迭代器遍歷的是開始遍歷那一刻拿到的集合拷貝,在遍歷期間原集合發生的修改迭代器是不知道的。
場景:java.util.concurrent包下的容器都是安全失敗,可以在多線程下并發使用,并發修改。
那么平常我們如何去規避這種情況呢?
這里有兩種解決方案:
方案一:在遍歷過程中所有涉及到改變modCount值得地方全部加上synchronized或者直接使用Collections.synchronizedList(不推薦)
方案二:使用CopyOnWriteArrayList來替換ArrayList。
CopyOnWriteArrayList為什么能解決這個問題呢?CopyOnWrite容器即寫時復制的容器。通俗的理解是當我們往一個容器添加元素的時候,不直接往當前容器添加,而是先將當前容器進行Copy,復制出一個新的容器,然后新的容器里添加元素,添加完元素之后,再將原容器的引用指向新的容器。CopyOnWriteArrayList中add/remove等寫方法是需要加鎖的,目的是為了避免Copy出N個副本出來,導致并發寫。但是。CopyOnWriteArrayList中的讀方法是沒有加鎖的。
我們只需要記住一句話,那就是CopyOnWriteArrayList是線程安全的,所以我們在多線程的環境下面需要去使用這個就可以了。關于CopyOnWriteArrayList更加深入的用法,會在以后的章節中去解釋說明。
fail-fast快速失敗案例:
1 public classFailFastDemo {2 private static List list = new ArrayList();3
4 public static voidmain(String[] args) {5 //兩個線程對同一個ArrayList進行操作
6 newThreadOne().start();7 newThreadTwo().start();8 }9 //輸出list中的值
10 private static voidprintAll(){11 try{12 String value = null;13 Iterator iterator =list.iterator();14 while(iterator.hasNext()){15 value =(String) iterator.next();16 System.out.println("list 中的值:"+value);17 Thread.sleep(1000);18 }19 } catch(InterruptedException e) {20 e.printStackTrace();21 }22 }23 //線程一:向list中依次添加數據,然后printAll整個list
24 private static class ThreadOne extendsThread{25 public voidrun() {26 for (int i = 0; i < 6; i++) {27 list.add(String.valueOf("線程一:Java的架構師技術棧"+i));28 printAll();29 }30 }31 }32 //線程二:對ArrayList進行同樣的操作
33 private static class ThreadTwo extendsThread{34 public voidrun() {35 for (int i = 0; i < 6; i++) {36 list.add(String.valueOf("線程二:Java的架構師技術棧"+i));37 printAll();38 }39 }40 }41 }
更改后:使用CopyOnWriteArrayList替代ArrayList來避免異常發生
1 public classFailFastDemo {2 //private static List list = new ArrayList();3 //使用CopyOnWriteArrayList替代ArrayList
4 private static List list = new CopyOnWriteArrayList();5 public static voidmain(String[] args) {6 //兩個線程對同一個ArrayList進行操作
7 newThreadOne().start();8 newThreadTwo().start();9 }10 //輸出list中的值
11 private static voidprintAll(){12 try{13 String value = null;14 Iterator iterator =list.iterator();15 while(iterator.hasNext()){16 value =(String) iterator.next();17 System.out.println("list 中的值:"+value);18 Thread.sleep(1000);19 }20 } catch(InterruptedException e) {21 e.printStackTrace();22 }23 }24 //線程一:向list中依次添加數據,然后printAll整個list
25 private static class ThreadOne extendsThread{26 public voidrun() {27 for (int i = 0; i < 6; i++) {28 list.add(String.valueOf("線程一:Java的架構師技術棧"+i));29 printAll();30 }31 }32 }33 //線程二:對ArrayList進行同樣的操作
34 private static class ThreadTwo extendsThread{35 public voidrun() {36 for (int i = 0; i < 6; i++) {37 list.add(String.valueOf("線程二:Java的架構師技術棧"+i));38 printAll();39 }40 }41 }42 }
運行結果無異常:
總結
以上是生活随笔為你收集整理的fail safe java_Java中快速失败(fail-fast)和安全失败(fail-safe)的区别?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 文本特征选择 java代码_文本分类入门
- 下一篇: java socket负载均衡_Java