1. 程式人生 > >java中ConcurrentModificationException異常分析

java中ConcurrentModificationException異常分析

jdk文件解釋:Iterator和ListIterator迭代器是快速失敗的,在迭代器建立之後,如果從結構上對列表進行修改,除非通過迭代器自身的 remove 或 add 方法,其他任何時間任何方式的修改,迭代器都將丟擲 ConcurrentModificationException。因此,面對併發的修改,迭代器很快就會完全失敗,而不冒將來不確定的時間任意發生不確定行為的風險。 下面通過分析LinkedList的原始碼來分析這種現象出現的原因,其他容易的類似: 首先給出AbstractList的定義:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {}

AbstractList類中定義了一個屬性:protected transient int modCount = 0,該屬性定義了列表結構上(增刪)被修改的次數。 同時該類中定義瞭如下方法:
public Iterator<E> iterator() {
     return new Itr();
}

其中Itr類的定義如下:
   private class Itr implements Iterator<E> {
        /**
         * 此處只列出相關的屬性和方法
         */
        int expectedModCount = modCount;
        public E next() {
            checkForComodification();
            try {
                int i = cursor;
                E next = get(i);
                lastRet = i;
                cursor = i + 1;
                return next;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException();
            }
        }

        public void remove() {
            if ( lastRet < 0)
                throw new IllegalStateException();
                checkForComodification();

            try {
                AbstractList.this.remove( lastRet);
                if ( lastRet < cursor)
                    cursor--;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {
                throw new ConcurrentModificationException();
            }
        }

        final void  checkForComodification (){
            if ( modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

可以看出當獲取當前物件的迭代器時,迭代器會將此時的modCount儲存到expectedModCount,然後每次進行結構上的修改之前都會呼叫checkForComodification ()來檢查expectedModCount是否等於modCount,若相等則說明迭代器生成之後沒有通過呼叫原始連結串列的remove()等方法修改連結串列結構(若連結串列自己修改了結構如刪除掉一個節點,會導致迭代器遍歷過程中訪問該節點時發現節點為空導致異常,所以為防止該異常,會盡早去發現是否可能出現該問題,從而進行“快速失敗”丟擲ConcurrentModificationException),然後呼叫LinkedList中的remove()方法來修改結構(下面會給出該方法,在該方法中會修改modCount++),結構修改之後迭代器需要修改expectedModCount以保證兩者始終相等。
LinkedList的類定義:
public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable{}
public abstract class AbstractSequentialList<E> extends AbstractList<E> {}

LinkedList中刪除一個節點的程式碼如下:
  /**
     * Unlinks non -null node x.
     */
    E unlink (Node<E> x) {
        // assert x != null;
        final E element = x. item;
        final Node<E> next = x. next;
        final Node<E> prev = x. prev;

        if (prev == null) {
            first = next;
        } else {
            prev. next = next;
            x. prev = null;
        }

        if (next == null) {
            last = prev;
        } else {
            next. prev = prev;
            x. next = null;
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }

正如jdk文件所說,ConcurrentModificationException異常的“快速失敗”機制是不能得到保證的,一般來說,存在不同步的併發修改時,不可能作出任何硬性保證。快速失敗迭代器盡最大努力丟擲ConcurrentModificationException。因此,編寫依賴於此異常的程式的方式是錯誤的,正確做法是:迭代器的快速失敗行為應該僅用於檢測程式錯誤。

相關推薦

javaConcurrentModificationException異常分析

jdk文件解釋:Iterator和ListIterator迭代器是快速失敗的,在迭代器建立之後,如果從結構上對列表進行修改,除非通過迭代器自身的 remove 或 add 方法,其他任何時間任何方式的

java.util.ConcurrentModificationException異常 分析

ArrayList是java開發時非常常用的類,常碰到需要對ArrayList迴圈刪除元素的情況。這時候大家都不會使用foreach迴圈的方式來遍歷List,因為它會拋java.util.ConcurrentModificationException異常。比如下面的程式碼就會拋這個異常: 1234567

java編程異常分析及面向對象的思考總結[圖]

目錄 內部 釋放資源 包括 sta overload 普通 none 命名 java編程中的異常分析及面向對象的思考總結[圖]1.異常:程序中出現的不正常現象。2.異常的由來:程序在運行的過程中出現了不正常的情況,程序把它看成對象提取了屬性行為(名字,原因,位置等信息)形成

java異常處理

功能 編譯 重寫 exce 有一個 人人 關系 構造 per   計算機語言程序開發中異常幾乎是人人都會出現的問題,可以這麽說:沒有沒有異常的程序!所以,計算機語言中異常處理是十分重要的一塊,糾錯能力也是每個程序員必須具備的基本能力!   異常處理的三種處理方式:   一、

Java異常

執行 代碼塊 結束運行 提示 包含 用戶輸入 控制 語句 自定義   作為程序開發人員,我們必然都見過代碼運行後拋出的異常。初學者見到程序運行後沒有出現預期的結果,而是出現了看都看不懂的異常提示,心都要碎了。編程人員中流傳著一句話,沒有無異常的代碼。雖然說得比較絕對,但是足

Java異常和處理詳解

stat 一個 局部變量 lose 出了 object sta tof .html 原文出處:代碼鋼琴家 簡介 程序運行時,發生的不被期望的事件,它阻止了程序按照程序員的預期正常執行,這就是異常。異常發生時,是任程序自生自滅,立刻退出終止,還是輸出錯誤給用戶?或者用C語

java異常(一)

數組 col logs exception 並且 test 但是 blog ring java異常的概念 執行期的錯誤(javac xxx.java) 運行期的錯誤(java xxx) 這裏講的是運行期出現的錯誤 class TestEx { public s

Java異常處理機制

條件 order 什麽是 浪費 sun color 越界 details 區域 基本框架如下Java中的異常處理機制只要實現自Throwable接口,繼承關系如下: 如上圖可以看出這個機制的處理對象主要分為兩種:主要區別error 表示恢復不是不可能但很困難的情況下的

初探Java異常處理

html try語句 捕獲 內部錯誤 b2c log fcm src size ? Java中的異常有以下幾種: 1)?Error:Java運行時的內部錯誤。 2)

java異常類型以及區別????

修復 illegal try 所有 修改 cep erro 原因 以及 一、引言   根據JDK的文檔我們能夠找到異常所在的包:java.lang.Throwable中,Throwable是所有異常類的根類,error是錯誤,在java.lang.error中,而Excep

JAVA異常處理及日誌(log4j為例)的使用

ble 包括 啟動 fatal try-catch stat 異常捕獲 資源 大於 Java的異常 1.Java中所有異常和錯誤的基類:Throwable     Throwable error        Exception       (檢查時異常)(運行時

Java異常與錯誤處理

ror sys lse AC alt xtend tro ima onu 編譯型異常和運行時異常 編譯時異常是指程序正確 而由外界條件不滿足而產生的異常 java 中要求必須去捕捉住這類異常 不然無法通過編譯 運行時異常是指程序存在著bug

深入理解Java的逃逸分析

end 代碼 堆內存 解決 永遠 例子 append 解釋器 return 在Java的編譯體系中,一個Java的源代碼文件變成計算機可執行的機器指令的過程中,需要經過兩段編譯,第一段是把.java文件轉換成.class文件。第二段編譯是把.class轉換成機器指令的過程。

java異常 try catch

bsp 17. 輸出 pro war 提示 編譯 無法 .get 1.學習異常的原因? 如果沒有異常處理機制,那麽程序的一點小問題,都會導致【程序終止運行】。實際開發中顯然是不可能的,所以異常對於程序來說是非常重要的。 2.處理異常的方式: A.if結

java捕獲異常

try n) ava nbsp 輸出 語句 runtime alt 線程 java的異常類都繼承自Throwable類。Throwable主要包括兩個大類。Error類和Exception類。Error類異常無法捕獲(是JVM異常或線程死鎖)。Exception分兩類非檢查

Java 處理異常的 9 個最佳實踐

lan method 永遠 是否 res ati 是你 dex mes 在本文中,作者介紹了9個處理異常的最佳方法與實踐,以舉例與代碼展示結合的方式,讓開發者更好的理解這9種方式,並指導讀者在不同情況下選擇不同的異常處理方式。 以下為譯文: Java中的異常處理不是一個簡單

Java異常處理、泛型!圖文講解

  1.異常: 非正常的情況,改變程式的執行流程 堆疊提示順序 Throwable Error 錯誤用程式碼處理不了 Exception異常 用程式碼可以處理執行時異常:RuntimeExcept

JAVA各種異常總結

1.java.lang.nullpointerexception這個異常大家肯定都經常遇到,異常的解釋是"程式遇上了空指標",簡單地說就是呼叫了未經初始化的物件或者是不存在的物件,這個錯誤經常出現在建立圖片,呼叫陣列這些操作中,比如圖片未經初始化,或者圖片建立時的路徑錯誤等等。對陣列操作中出現空

java排序原始碼分析(JDK1.8)

List排序 在開發過程中常用的是jdk自帶的排序 Collections.sort(List<T> list, Comparator<? super T> c); 開啟原始碼如下: @SuppressWarnings({"unchecked",

【轉】java輸出異常資訊

做java開發的時候,經常會遇到程式碼拋異常後,需要把異常資訊儲存到資料庫或者上傳到雲伺服器做cache分析。這時候就需要獲取異常的堆疊資訊(詳細錯誤資訊)。 有的人用e.getMessage()來獲取異常資訊,但是這樣獲取到的資訊內容並不全,而且有時候為空。我們可以用下面方法來獲取。