根據Java的容器說說“快速報錯” (fail-fast)

Java容器有一種保護機制,能夠防止多個線程修改同一個容器的內容。
這個機制主要是應用到迭代器上,如果你使用迭代器進行遍歷,那麼此時有另一個線程修改了這個數據結構,比如此時,別的線程,增加,刪除了某個元素,這個是Java的非同步容器是不允許的同時兩個的操作。

何爲快速報錯?

其實就是根據這個容器的如何你使用的不這正確,那麼就會拋出錯誤,程序就不會運行下去了。如果此時不泡錯,那麼程序就會用錯誤的數據得到一個錯誤的結果,這樣無疑是一錯再錯,錯上加錯。這樣明顯不符合我們設計代碼的原來的設想啊。
你可以先運行下面這段代碼,運行環境JDK 8+:

public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<>(){{
        for (int i = 0; i < 1000; i++) {
            add(i + "");
        }
    }};
    Iterator iterator = list.iterator();
    Thread t1 = new Thread(() -> {
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    });
    Thread t2 = new Thread(() -> {
        for (int i = 0; i < 500; i++) {
            iterator.next();
            iterator.remove();
        }
    });
    t1.start();
    t2.start();
}

元素儘量多一些,刪除的也儘量多一些,不然你的機器性能太好的話,還真的不會報錯。。。不出意外的話,會出現ConcurrentModificationException這種錯誤,這個錯誤本身就是因爲快速報錯機制引起的。

爲什麼不允許

你設想一種情形,比如現在你有一個ArrayList,你根據這個容器的目前的容量進行迭代獲取容器的所有元素,但是這個時候有另一個線程,刪除了一些元素,但是你的這個線程不知道啊,這個線程依然按照原來的長度進行遍歷,那麼這個時候是肯定報一個數組越界的錯誤。這個明顯不是我們想要的結果,所以就要防止這種的發生。
所以在一個線程迭代容器的同時,另一個線程進行修改,是不允許的,會出現線程安全問題。
那麼如果我想要在單線程的環境下,迭代的同時刪除,那麼我要怎麼做?
用迭代器裏面的刪除方式,這樣會安全許多。
一般來說我們迭代數組用以下兩種方式

ArrayList<String> list = new ArrayList<>() {{
	for (int i = 0; i < 1000; i++) {
		add(i + "");
	}
}};
// 1 根據容器的size進行迭代
for (int i = 0, len  = list.size(); i < len; i++) {
	System.out.println(i);
}
// 2 使用迭代器迭代
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
	System.out.println(iterator.next());
}

使用第二種的迭代器的時候,使用iterator.remove()來進行刪除對象,這樣是允許的。當然,你一點用第一種,修改一下代碼也是可以用的,就是記得判斷變成每次獲取長度,還有記得調整i的值就行。

爲什麼只有某些迭代器有這種報錯機制,別的沒有?

還是你問爲什麼不允許,那我只能猜測一下了,併發容器和非併發容器其實早就JDK的設計者們分的明明白白了,所以針對併發和非併發的場景有着明確的數據結構。比如,ArrayList就是在單線程的環境下使用的,效率好; CopyOnWriteArrayList 則是在多線程的環境下使用的,效率雖然差些,但是保證讀和寫的線程安全。

發佈了139 篇原創文章 · 獲贊 13 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章