java返回fail_Java集合中的fail-fast(快速失败)机制详解
簡介
我們知道Java中Collection接口下的很多集合都是線程不安全的, 比如 java.util.ArrayList不是線程安全的, 因此如果在使用迭代器的過程中有其他線程修改了list,那么將拋出ConcurrentModificationException,這就是所謂fail-fast策略。
這一策略在源碼中的實(shí)現(xiàn)是通過 modCount 域,modCount 顧名思義就是修改次數(shù),對ArrayList 內(nèi)容的修改都將增加這個值,那么在迭代器初始化過程中會將這個值賦給迭代器的 expectedModCount。在迭代過程中,判斷 modCount 跟 expectedModCount 是否相等,如果不相等就表示已經(jīng)有其他線程修改了 list
注意到 modCount 聲明為 volatile,保證線程之間修改的可見性。
modCount和expectedModCount
modCount和expectedModCount是用于表示修改次數(shù)的,其中modCount表示集合的修改次數(shù),這其中包括了調(diào)用集合本身的add, remove, clear方法等修改方法時進(jìn)行的修改和調(diào)用集合迭代器的修改方法進(jìn)行的修改。而expectedModCount則是表示迭代器對集合進(jìn)行修改的次數(shù)。
設(shè)置expectedModCount的目的就是要保證在使用迭代器期間,list對象只能有這一個迭代器對list進(jìn)行修改。
在創(chuàng)建迭代器的時候會把對象的modCount的值傳遞給迭代器的expectedModCount:
private class ListItr implements ListIterator {
private Node lastReturned;
private Node next;
private int nextIndex;
private int expectedModCount = modCount;
如果創(chuàng)建多個迭代器對一個集合對象進(jìn)行修改的話,那么就會有一個modCount和多個expectedModCount,且modCount的值之間也會不一樣,這就導(dǎo)致了moCount和expectedModCount的值不一致,從而產(chǎn)生異常:
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
上面的代碼中的checkForComodification會檢查modCount和expectedModCount的值是否一致,不一致則拋出異常。
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
modCount是如何被修改的
// 添加元素到隊(duì)列最后
public boolean add(E e) {
// 修改modCount
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
// 添加元素到指定的位置
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
// 修改modCount
ensureCapacity(size+1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
// 添加集合
public boolean addAll(Collection extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
// 修改modCount
ensureCapacity(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
// 刪除指定位置的元素
public E remove(int index) {
RangeCheck(index);
// 修改modCount
modCount++;
E oldValue = (E) elementData[index];
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
elementData[--size] = null; // Let gc do its work
return oldValue;
}
// 快速刪除指定位置的元素
private void fastRemove(int index) {
// 修改modCount
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // Let gc do its work
}
// 清空集合
public void clear() {
// 修改modCount
modCount++;
// Let gc do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
也就是在對集合進(jìn)行數(shù)據(jù)的增刪的時候都會執(zhí)行modcount++, 那么如果一個線程還在使用迭代器遍歷這個list的時候就會發(fā)現(xiàn)異常, 發(fā)生 fail-fast(快速失敗)
fail-fast(快速失敗)和fail-safe(安全失敗)比較
Iterator的快速失敗是基于對底層集合做拷貝是淺拷貝,因此,它受源集合上修改的影響。java.util包下面的所有的集合類都是快速失敗的
而java.util.concurrent包下面的所有的類都是使用鎖實(shí)現(xiàn)安全失敗的。
快速失敗的迭代器會拋出ConcurrentModificationException異常,而安全失敗的迭代器永遠(yuǎn)不會拋出這樣的異常。
fail-fast解決什么問題
fail-fast機(jī)制,是一種錯誤檢測機(jī)制。
它只能被用來檢測錯誤,因?yàn)镴DK并不保證fail-fast機(jī)制一定會發(fā)生。只是在多線程環(huán)境下告訴客戶端發(fā)生了多線程安全問題.
所以若在多線程環(huán)境下使用fail-fast機(jī)制的集合,建議使用“java.util.concurrent包下的類”去取代“java.util包下的類”。
如何解決fail-fast事件
ArrayList對應(yīng)的CopyOnWriteArrayList進(jìn)行說明。我們先看看CopyOnWriteArrayList的源碼:
public class CopyOnWriteArrayList
implements List, RandomAccess, Cloneable, java.io.Serializable {
...
// 返回集合對應(yīng)的迭代器
public Iterator iterator() {
return new COWIterator(getArray(), 0);
}
...
private static class COWIterator implements ListIterator {
private final Object[] snapshot;
private int cursor;
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
// 新建COWIterator時,將集合中的元素保存到一個新的拷貝數(shù)組中。
// 這樣,當(dāng)原始集合的數(shù)據(jù)改變,拷貝數(shù)據(jù)中的值也不會變化。
snapshot = elements;
}
public boolean hasNext() {
return cursor < snapshot.length;
}
CopyOnWriteArrayList是自己實(shí)現(xiàn)Iterator, 并且CopyOnWriteArrayList的Iterator實(shí)現(xiàn)類中,沒有所謂的checkForComodification(),更不會拋出ConcurrentModificationException異常
CopyOnWriteArrayList在進(jìn)行新建COWIterator時,將集合中的元素保存到一個新的拷貝數(shù)組中。這樣,當(dāng)原始集合的數(shù)據(jù)改變,拷貝數(shù)據(jù)中的值也不會變化。
總結(jié)
到此這篇關(guān)于Java集合中的fail-fast(快速失敗)機(jī)制的文章就介紹到這了,更多相關(guān)Java集合fail-fast(快速失敗)機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
總結(jié)
以上是生活随笔為你收集整理的java返回fail_Java集合中的fail-fast(快速失败)机制详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 信息学奥赛一本通 提高篇 第5章 矩阵乘
- 下一篇: 科学启蒙课