CopyOnWrite特点
- 一般用于读多写少的情况,用于提高读取数据的效率
- 注意的是,读数据并不能保证实时性,因为读取时,读取的是旧数组的数据
- 缺点是:占用内存(每添加一次就需要复制一个数据)和数据一致性问题(不能保证实时数据)
以CopyOnWriteArrayList源码进行分析
属性
// 显式操作的重入锁对象
final transient ReentrantLock lock = new ReentrantLock();
// 用来存储元素的对象数组
private transient volatile Object[] array;
构造器
// 默认构造器
public CopyOnWriteArrayList() {
setArray(new Object[0]); // 数组长度为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);
}
// 给定一个数组来构建CopyOnWriteArrayList实例
public CopyOnWriteArrayList(E[] toCopyIn) {
setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}
写方法【写时复制】
add(E e) - 添加一个元素
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();
}
}
add(int index, E element) - 添加一个元素到指定位置
public void add(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 获取内部对象数组(旧对象数组)
Object[] elements = getArray();// =========== 旧对象数组
// 旧对象数组长度
int len = elements.length;
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+", Size: "+len);
Object[] newElements;
int numMoved = len - index; // 对象数组最后的下标
if (numMoved == 0)
// 返回一个全新的数组
newElements = Arrays.copyOf(elements, len + 1); // =========== 新对象数组
else {
newElements = new Object[len + 1]; // =========== 新对象数组
// 将elements下标为0开始的元素复制到newElements下标为[0,index)处
System.arraycopy(elements, 0, newElements, 0, index);
// 将elements下标为index开始的元素复制到newElements下标为[index+1,numMoved)处
System.arraycopy(elements, index, newElements, index + 1,numMoved);
}
// 将element插入到newElements的index处
newElements[index] = element;
// 替换内部对象数组的引用
setArray(newElements);
} finally {
lock.unlock();
}
}
set(int index, E element) - 替换指定位置的元素
public E set(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray(); // =========== 旧对象数组
// index位置的旧元素
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();
}
}
读方法
final Object[] getArray() {
return array; // 获取内部对象数组
}
// 根据下标获取元素
public E get(int index) {
return get(getArray(), index);
}
@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {
return (E) a[index];
}
使用测试
public class Main {
public static void main(String[] args) {
CopyOnWriteArrayList<String> cow = new CopyOnWriteArrayList<String>();
cow.add("a1");
cow.add("a2");
cow.add(1, "a1.5");
System.out.println("=====" + cow + "=====");
cow.set(1, "hehe");
System.out.println("=====" + cow + "=====");
/**
* output:
=====[a1, a1.5, a2]=====
=====[a1, hehe, a2]=====
*/
}
}