目的
フォローアップのインタビューレビューの便宜のために、ここに個人的な学習ノートの記録を記載します
学習教材のソース
核となる知識のポイント
フェイルファースト: 同時変更はサポートされていません。同時変更が発生した場合、ConcurrentModificationException が直接スローされます。フェール
セーフ: 上記とは異なり、一貫性を犠牲にして同時変更がサポートされます。
チェックがあります: checkForComodification()
ArrayList はFail-fasの代表的なもの、トラバース 同時変更には対応しておらず、すぐに失敗する
CopyOnWritArrayList はフェールセーフの代表的なもので、トラバース中に変更可能、読み取り分離の原理そして書くこと。
ソースコードの抜粋
ArrayListのソースコードの一部
ExpectedModCount と modCount を比較し、等しくない場合は、同時変更例外 (ConcurrentModificationException) を直接スローします。
イテレータのソースコードの一部:
/**
* An optimized version of AbstractList.Itr
*/
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;
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();
}
// 逻辑很简单就是2个modCount做一个对比
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
ソースコードの CopyOnWriteArrayList 部分
イテレータ部分のソースコード
スナップショットの配列に対するすべての操作は次のとおりです。
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;
}
@SuppressWarnings("unchecked")
public E next() {
if (! hasNext())
throw new NoSuchElementException();
return (E) snapshot[cursor++];
}
}
addメソッドの実装原理
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
// 赋值原来的集合, 容量+1
Object[] newElements = Arrays.copyOf(elements, len + 1);
// 新元素放到新集合最后
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
読み書き分離の考え方
CopyOnWriteArrayList は実際には 2 つの配列を保持します: 読み取り: スナップショットのため、実際に前の配列を読み取ります。書き込み: 新しいデータに書き込みます。元のスナップショット配列は、後で参照されない場合、自動的にガベージ コレクションされます。