目的
方便后续面试复习, 这里做一个个人学习笔记记录
学习资料来源
核心知识点
Fail-fast: 不支持并发修改, 如果出现并发修改, 直接抛出 ConcurrentModificationException
Fail-safe: 和上面相反, 支持并发修改, 牺牲一致性.
有检验: checkForComodification()
ArrayList 是Fail-fas的典型代表, 遍历的同时不支持修改, 尽快失败.
CopyOnWritArrayList 是Fail-safe的典型代表, 遍历的同时可以修改, 原理的读写分离.
源码摘录
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 部分源码
迭代器部分源码
这里都是在快照的数组上操作的: snapshot
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 其实维护了两个数组, 读: 其实是读之前的数组, 因为做了快照. 写: 写入到新的数据. 原来快照的数组后续没有被引用会自动被垃圾回收掉.