假如有兩個線程A與線程B的烁,線程A對集合C進(jìn)行迭代操作時(shí)诈闺,線程B改變了集合C的數(shù)據(jù)結(jié)構(gòu),此時(shí)會報(bào)出ConcurrentModificationException異常把曼,這就是Fail-Fast(快速失敗)漓穿,其為java中集合操作的快速檢錯機(jī)制。留下30分鐘叙赚,一看究竟僚饭。
一鳍鸵、Fail-Fast案例
public class FailFastTest {
private static List<Integer> list = new ArrayList<Integer>();
private static class ThreadOne extends Thread{
public void run(){
Iterator<Integer> it = list.iterator();
while(it.hasNext()){
Integer i = it.next();
System.out.println("ThreadOne is using : "+ i);
try {
Thread.sleep(5);
} catch (Exception e) {
e.getStackTrace();
}
}
}
}
private static class ThreadTwo extends Thread{
public void run(){
int i = 0;
while(i < 6){
if (i == 3 ) {
list.remove(i);
}
i++;
}
}
}
public static void main(String[] args) {
for(int i=0 ; i < 10; i++ ){
list.add(i);
}
new ThreadOne().start();
new ThreadTwo().start();
}
}
// 程序運(yùn)行后偿乖,報(bào)出異常,因?yàn)門hreadOne進(jìn)行迭代操作時(shí)媳禁,ThreadTwo對其進(jìn)行數(shù)據(jù)結(jié)構(gòu)的修改画切,程序報(bào)出異常提醒
二、源碼解讀
// JDK1.8內(nèi)ArrayList中迭代器源碼
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; // 由modCount決定毫别,值不會改變
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];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
// 在迭代器中的next()、remove()和forEachRemaining()方法中
// 均包含checkForComodification()钝计,而checkForComodification() // 方法
// 會拋出ConcurrentModificationException()異常私恬,
// 關(guān)鍵在于檢測modCount與expectedModCount是否相同
//JDK1.8內(nèi)ArrayList中包含的方法,大都包含modCount++操作
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
- 對ArrayList進(jìn)行迭代時(shí)疫衩,會不斷檢測expectedCount和modCount的值闷煤,如果其他操作修改了全局的modCount的值,則會報(bào)出異常鲤拿;
三、解決方案
- 推薦使用CopyOnWriteArrayList近顷,先來解讀一下源碼:
// JDK1.8內(nèi)CopyOnWriteArrayList中迭代器不進(jìn)行expectedCount和modCount檢查
static final class COWIterator<E> implements ListIterator<E> {
/** Snapshot of the array */
private final Object[] snapshot;
/** Index of element to be returned by subsequent call to next. */
private int cursor;
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
public boolean hasNext() {
return cursor < snapshot.length;
}
public boolean hasPrevious() {
return cursor > 0;
}
@SuppressWarnings("unchecked")
public E next() {
if (! hasNext())
throw new NoSuchElementException();
return (E) snapshot[cursor++];
}
// JDK1.8內(nèi)CopyOnWriteArrayList中迭代器不進(jìn)行expectedCount和modCount檢查
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
// add()方法生音,本質(zhì)是對于原數(shù)組的copy,不會存在修改集合數(shù)據(jù)結(jié)構(gòu)的操作窒升,其他方法類似
CopyOnWriteArrayList的核心原理:對于Array的操作(增刪改查)缀遍,都是基于復(fù)制到新copy數(shù)組,不會改變迭代器的對象饱须,修改完成后改變原有數(shù)據(jù)的引用即可域醇。
今天就暫時(shí)到這里啦!畢業(yè)季在匆忙與顧念中離去冤寿,回首一年前的畢業(yè)季歹苦,做好眼前事,珍惜校園時(shí)光殴瘦。