public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
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();
}
}
讨论两个问题
1、add如果不加锁,add 方法会产生的线程安全问题
答:其他线程会改变list的size,数组的容量,等等,会导致不管是if比较size也好,扩容也好,非原子操作,会导致每个步骤跟你期望的不一样,导致各种并发问题。
2、add方法直接操作原数组,会不会影响到get
也就是如下操作,能不能保证线程安全性。
public class TestList extends ArrayList {
ReentrantLock lock = new ReentrantLock();
@Override
public boolean add(Object o) {
lock.lock();
boolean add = super.add(o);
lock.unlock();
return add;
}
}
答:看ArrayList源代码,我个人感觉,可以保证,所以,为什么CopyOnWriteArrayList要重新复制一份数组出来,做这种低效能的事情,暂时存疑。
有一点,copyOnWriteArrayList的生成迭代器不会产生ConcurrentModifyException。
可以看到CopyOnWriteArrayList的缺点。
1、add是串行的,比较慢。也就是适合,写少读多的情况。
2、如果数组容量很大,每次add都要复制,会比较慢。
看ArrayList
可以看到,arraylist会进行扩容,每次扩容1.5倍。
public boolean add(E e) {
//
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 扩容1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 扩容后还是小了,则直接为给定容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果容量达到了最大容量,则直接给最大容量
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
网友评论