概述
CopyOnWriteArrayList
CopyOnWriteArrayList允许线程并发访问读操作,这个时候是没有加锁限制的,性能较高。而写操作的时候,则首先将容器复制一份,然后再新的副本上执行写操作,这个时候写操作是上锁的,结束之后再将原容器的引用指向新容器。注意:在上锁执行写操作的过程中,如果有需要读操作,会作用在原容器上。因此上锁的写操作不会影响到并发访问的读操作
构造方法
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
private static final long serialVersionUID = 8673264195747942595L;
//互斥锁
final transient ReentrantLock lock = new ReentrantLock();
//底层存储数据数组,只能通过getArray/setArray访问设置,volatile动态数组
private transient volatile Object[] array;
final Object[] getArray() {
return array;
}
final void setArray(Object[] a) {
array = a;
}
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
//传入Collection集合对象,将集合中元素存入CopyOnWriteArrayList
public CopyOnWriteArrayList(Collection<? extends E> c) {
Object[] elements;
if (c.getClass() == CopyOnWriteArrayList.class)
elements = ((CopyOnWriteArrayList<?>)c).getArray();
else {
elements = c.toArray();
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elements.getClass() != Object[].class)
elements = Arrays.copyOf(elements, elements.length, Object[].class);
}
setArray(elements);
}
//传入数组
public CopyOnWriteArrayList(E[] toCopyIn) {
setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}
}
插入
- 在写入过程中使用了互斥锁,所以同一时间只有一个线程在修改CopyOnWriteArrayList
- 增加元素并不是直接在原数组操作,而是在原数组的拷贝数组上添加元素的,添加完元素再调用setArray方法用新数组代替原始数组
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();
}
}
public void add(int index, E element) {
//互斥锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
//原始数组
Object[] elements = getArray();
int len = elements.length;
//检查index有效性
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+len);
//拷贝数组
Object[] newElements;
//从index到数组末尾要向后移动一位数组元素的个数
int numMoved = len - index;
//如果index==length,直接把原数组复制到新数组
if (numMoved == 0)
newElements = Arrays.copyOf(elements, len + 1);
//否则分成两段复制,原始数组index前面的元素位置一一对应赋值到新数组,原数组index开始的元素复制到
//新数组index+1到length+1,相当于依次后移。空出来的index就是新元素插入的位置
else {
newElements = new Object[len + 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
//插入新元素
newElements[index] = element;
setArray(newElements);
} finally {
lock.unlock();
}
}
public E set(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
E oldValue = get(elements, index);
if (oldValue != element) {
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len);
newElements[index] = element;
setArray(newElements);
} else {
setArray(elements);
}
return oldValue;
} finally {
lock.unlock();
}
}
删除
public E remove(int index) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
//数组index处要移除的元素
E oldValue = get(elements, index);
int numMoved = len - index - 1;
if (numMoved == 0)
setArray(Arrays.copyOf(elements, len - 1));
else {
Object[] newElements = new Object[len - 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
setArray(newElements);
}
return oldValue;
} finally {
lock.unlock();
}
}
查找
public E get(int index) {
return get(getArray(), index);
}
@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {
//返回数组index处位置
return (E) a[index];
}
优缺点
- 优点:读操作性能很高,因为无需任何同步措施,比较适用于读多写少的并发场景。在遍历传统的List时,若中途有别的线程对其进行修改,则会抛出ConcurrentModificationException异常。而CopyOnWriteArrayList由于其“读写分离”的思想,遍历和修改操作分别作用在不同的List容器,所以在使用迭代器进行遍历时候,也就不会抛出ConcurrentModificationException异常
- 缺点:一是内存占用问题,每次执行写操作都要将原容器拷贝一份,数据量大时,对内存压力较大,可能会引起频繁GC。二是无法保证实时性,Vector对于读写操作均加锁操作,可以保证读和写的一致性。而CopyOnWriteArrayList由于其实现策略的原因,写和读分别作用在新老不同容器上,在写操作执行过程中,读不会阻塞但读取到的却是老容器的数据
线程安全的List
- Vector(使用synchronized修饰方法)
- Collections.synchronizedList
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
- CopyOnWriteArrayList
最后
以上就是无辜心情为你收集整理的Java集合——CopyOnWriteArrayList的全部内容,希望文章能够帮你解决Java集合——CopyOnWriteArrayList所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复