一、概念
类定义:
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
- 实现了List接口,拥有一组List通用的操作。
- 实现了RandomAccess接口,可进行随机访问。
- 实现了Cloneable接口,可进行浅层次拷贝。
- 实现了Serializable接口,可进行序列化。
特点:
- 读取操作完全不用加锁。
- 写入操作不会阻塞读取操作。
- 只有写入操作与写入操作需要进行同步。
原理:
CopyOnWriteArrayList类的写操作是通过创建底层数组的新副本来实现的。当List需要被修改的时候,并不直接修改原有数组对象,而是对原有数据进行一次拷贝,将修改的内容写入副本中。写完之后,再将修改完的副本替换成原来的数据,这样就可以保证写操作不会影响读操作了。
在多线程环境中,如果线程A执行写操作(如add、set等)之前线程B执行了读操作(如get、iterator等),那么在线程A执行写操作完成并更新了底层数组后,线程B执行读操作使用的底层数组仍然是旧的那一份,这就是弱一致性。
二、使用
//TestCopyOnWriteArrayList
public class TestCopyOnWriteArrayList {
private static final String TAG = "CopyOnWriteArrayList";
private CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList();
public void testAdd() {
list.add(null);
list.add("AAA");
list.add("BBB");
list.addIfAbsent("CCC");
list.addIfAbsent("CCC");
Log.d(TAG, "zwm, add list: " + list);
}
public void testSet() {
list.set(1, "AAAAA");
Log.d(TAG, "zwm, set list: " + list);
}
public void testGet() {
Log.d(TAG, "zwm, get index 1: " + list.get(1));
}
public void testRemove() {
list.remove(null);
Log.d(TAG, "zwm, remove list: " + list);
}
public void testSize() {
Log.d(TAG, "zwm, size: " + list.size());
}
public void testListIterator() {
ListIterator<String> listIterator = list.listIterator();
while (listIterator.hasNext()) {
Log.d(TAG, "zwm, listIterator item: " + listIterator.next());
}
}
}
//测试代码
private void testMethod() {
Log.d(TAG, "zwm, testMethod");
TestCopyOnWriteArrayList testCopyOnWriteArrayList = new TestCopyOnWriteArrayList();
testCopyOnWriteArrayList.testAdd();
testCopyOnWriteArrayList.testSet();
testCopyOnWriteArrayList.testGet();
testCopyOnWriteArrayList.testRemove();
testCopyOnWriteArrayList.testSize();
testCopyOnWriteArrayList.testListIterator();
}
//输出log
2019-08-17 11:05:55.960 zwm, testMethod
2019-08-17 11:05:55.962 zwm, add list: [null, AAA, BBB, CCC]
2019-08-17 11:05:55.962 zwm, set list: [null, AAAAA, BBB, CCC]
2019-08-17 11:05:55.962 zwm, get index 1: AAAAA
2019-08-17 11:05:55.962 zwm, remove list: [AAAAA, BBB, CCC]
2019-08-17 11:05:55.962 zwm, size: 3
2019-08-17 11:05:55.963 zwm, listIterator item: AAAAA
2019-08-17 11:05:55.963 zwm, listIterator item: BBB
2019-08-17 11:05:55.963 zwm, listIterator item: CCC
三、原理
重要参数
//锁对象
final transient ReentrantLock lock = new ReentrantLock();
//存放数据的数组
private transient volatile Object[] array;
构造函数
//创建一个元素个数为0的CopyOnWriteArrayList
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);
}
//创建一个具有指定的toCopyIn数组的元素的CopyOnWriteArrayList
public CopyOnWriteArrayList(E[] toCopyIn) {
setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}
public boolean add(E e)
//插入一个元素
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock(); //使用ReentrantLock加锁
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(); //在finally块中释放锁
}
}
public E set(int index, E element)
//修改索引位置的元素
public E set(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock(); //使用ReentrantLock加锁
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 {
// Not quite a no-op; ensures volatile write semantics
setArray(elements);
}
return oldValue;
} finally {
lock.unlock(); //在finally块中释放锁
}
}
public E get(int index)
//获取索引位置的元素,不需要加锁
public E get(int index) {
return get(getArray(), index);
}
private E get(Object[] a, int index) {
return (E) a[index];
}
public boolean remove(Object o)
//删除与对象o相等的元素
public boolean remove(Object o) {
Object[] snapshot = getArray();
int index = indexOf(o, snapshot, 0, snapshot.length);
return (index < 0) ? false : remove(o, snapshot, index);
}
private boolean remove(Object o, Object[] snapshot, int index) {
final ReentrantLock lock = this.lock;
lock.lock(); //使用ReentrantLock加锁
try {
Object[] current = getArray(); //获取底层数组
int len = current.length;
if (snapshot != current) findIndex: {
int prefix = Math.min(index, len);
for (int i = 0; i < prefix; i++) {
if (current[i] != snapshot[i] && eq(o, current[i])) {
index = i;
break findIndex;
}
}
if (index >= len)
return false;
if (current[index] == o)
break findIndex;
index = indexOf(o, current, index, len);
if (index < 0)
return false;
}
Object[] newElements = new Object[len - 1]; //创建新数组
System.arraycopy(current, 0, newElements, 0, index); //执行拷贝操作
System.arraycopy(current, index + 1,
newElements, index,
len - index - 1);
setArray(newElements); //更新底层数组,使用新数组替换旧数组
return true;
} finally {
lock.unlock(); //在finally块中释放锁
}
}
public int size()
//获取元素个数
public int size() {
return getArray().length;
}
网友评论