bag系列ConcurrentModificationException---併發修改異常

星空下的213發表於2020-12-24

一.異常結構

異常機制:

​ 異常機制是指當程式出現錯誤後,程式如何處理。具體來說,異常機制提供了程式退出的安全通道。當出現錯誤後,程式執行的流程發生改變,程式的控制權轉移到異常處理器。

程式錯誤:

​ 程式錯誤分為三種:1.編譯錯誤;2.執行時錯誤;3.邏輯錯誤。
(1)編譯錯誤:是因為程式沒有遵循語法規則,編譯程式能夠自己發現並且提示我們錯誤的原因和位置。
(2)執行時錯誤:是因為程式在執行時,執行環境發現了不能執行的操作。 (3)邏輯錯誤:是因為程式沒有按照預期的邏輯順序執行。

異常也就是指程式執行時發生錯誤,而異常處理就是對這些錯誤進行處理和控制。

在這裡插入圖片描述

二.異常收集

序號異常名稱異常描述
1java.lang.NullPointerException空指標異常:物件為空,並且呼叫相應方法。
2java.lang.ClassNotFoundException找不到指定類
3java.lang.ArrayIndexOutOfBoundsException陣列下標越界
4java.lang.NumberFormatException:數字格式化異常
5java.lang.ArithmeticException:數學運算異常
6java.lang.StackOverflowError記憶體空間溢位錯誤,方法遞迴呼叫中,經常發生
7java.lang.ClassCastException型別轉換異常,向下轉型中經常發生
8java.text.ParseException時間格式化異常,SimpleDateFormart中經常發生
9java.util.InputMismatchException輸入型別不匹配異常
10java.util.ConcurrentModificationException併發修改異常

三.宣告和丟擲異常

1.使用throws宣告異常

​ 運用於方法宣告之上,用於表示當前方法不處理異常,而是提醒該方法的呼叫者來處理異常

public void fun1() throws ParseException {
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date date = format.parse("2020-10-12");
}

2.使用throw丟擲異常

​ throw用在方法內,用來丟擲一個異常物件,將這個異常物件傳遞到呼叫者處,並結束當前方法的執行。

public void fun2() {
     throw new NullPointerException("空指標異常");  
     System.out.println("hello world");//會報紅,提示unreachable statement,該語句不可能被執行
}

案例:

建立一個集合,存入兩個元素,“曹操” “貂蟬” ,遍歷集合,如果發現遍歷的元素是貂蟬,就
向集合中新增一個“呂布”

package case01;

import java.util.ArrayList;
import java.util.Iterator;

public class Demo03 {
    public static void main(String[] args) {
        ArrayList list=new ArrayList();
        list.add("曹操");
        list.add("貂蟬");

        Iterator it=list.iterator();
        while (it.hasNext()){
            if (it.next().equals("貂蟬")){
                list.add("呂布");
                list.remove("11");
               // break;
            }
        }
        for (int i = 0; i <list.size() ; i++) {
            if(list.get(i).equals("貂蟬")){
                list.add("呂布");
            }
        }
        System.out.println(list);
    }
}

報錯如下所示:

baocuo

報錯原因分析如下:

基本上ArrayList採用modCoun屬性來維護自已的狀態,而Iterator採用texpectedModCount來來維護自已的狀態。當modCount出現變化時,texpectedModCount並不一定能夠得到同步,除非這種變化是Iterator主動導致的。

從下面的程式碼可以看到當list.add()方法導致ArrayList列表發生變化時,他會更新modCount來同步這一變化。但其他方式導致的ArrayList變化,list是無法感知的。ArrayList自然也不會主動通知Iterator們,那將是一個繁重的工作。Iterator到底還是做了努力:為了防止狀態不一致可能引發的無法設想的後果,Iterator會經常做checkForComodification檢查,以防有變。如果有變,則以異常丟擲,所以就出現了上面的異常。

如果對正在被迭代的集合進行結構上的改變(即對該集合使用add、remove或clear方法),那麼迭代器就不再合法(並且在其後使用該迭代器將會有ConcurrentModificationException異常被丟擲).


        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        Itr() {}

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

結果圖如下所示:

可以通過以下的以下步驟檢視具體的內部實現

在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述

相關文章