温州专业营销网站制作,沈阳做公司网站的公司,西安人才网官网,西宁网站建设高端简介
CopyOnWriteArrayList是Java中的一个线程安全的集合类#xff0c;是ArrayList线程安全版本#xff0c;主要通过Copy-On-Write#xff08;写时复制#xff0c;简称COW#xff09;机制来保证线程安全。
Copy-On-Write机制核心思想#xff1a;向一个数组中添加数据时…简介
CopyOnWriteArrayList是Java中的一个线程安全的集合类是ArrayList线程安全版本主要通过Copy-On-Write写时复制简称COW机制来保证线程安全。
Copy-On-Write机制核心思想向一个数组中添加数据时不直接操作原始数组而是拷贝原始数组生成一份原始数组副本将需要添加的数据添加到原始数组副本中操作完成后再用原始数组副本直接替换原始数组从而保证多个线程同时操作原始数组时的线程安全。
应用场景
CopyOnWriteArrayList的主要应用场景
读多写少的场景CopyOnWriteArrayList允许多个线程同时对数据进行读操作但同一时刻只允许一个线程对数据进行写操作并且进行写操作时需要复制原始数据的副本造成空间和时间的浪费。允许读写数据时出现短暂不一致的场景CopyOnWriteArrayList写操作完成后需要使用更新后的数据副本替换原始数据有可能使CopyOnWriteArrayList中的数据出现短暂不一致。
实现原理
CopyOnWriteArrayList类的继承关系 Iterable接口CopyOnWriteArrayList类实现了Iterable接口使得它可以被迭代遍历。通过实现iterator()方法可以获取一个迭代器用于遍历集合中的元素。 Collection接口CopyOnWriteArrayList类继承了AbstractCollection类间接实现了Collection接口。通过实现Collection接口CopyOnWriteArrayList类获得了一些常用的集合操作方法比如判断是否包含某个元素、计算集合的大小等。 List接口CopyOnWriteArrayList类间接实现了List接口通过继承AbstractList类实现了List接口的一些方法。List接口定义了有序的集合CopyOnWriteArrayList类通过继承List接口可以按照索引进行访问和操作集合中的元素。 Cloneable接口CopyOnWriteArrayList类实现了Cloneable接口使得它可以进行克隆操作。通过实现clone()方法可以创建CopyOnWriteArrayList对象的副本。 Serializable接口CopyOnWriteArrayList类实现了Serializable接口使得它可以进行序列化操作。通过实现writeObject()和readObject()方法可以将CopyOnWriteArrayList对象转换为字节流并进行存储或传输。 RandomAccess接口CopyOnWriteArrayList类实现了RandomAccess接口表示它可以高效地随机访问元素。RandomAccess接口是一个标记接口用于标识实现该接口的类可以通过索引进行快速随机访问CopyOnWriteArrayList类通过实现该接口表明它可以高效地随机访问元素。
重要属性
CopyOnWriteArrayList类中有两个重要的属性 arrayObject数组用于存储CopyOnWriteArrayList中的数据。该属性使用transient和volatile关键字进行修饰 transient表示序列化CopyOnWriteArrayList对象时array属性不会被自动序列化。 volatile表示一个线程对CopyOnWriteArrayList数据的更新操作对其他线程可见。 lockReentrantLock锁用于保证多个线程同时对CopyOnWriteArrayList进行写操作的线程安全性。该属性也使用transient关键字进行修饰。
CopyOnWriteArrayList类定义代码如下
public class CopyOnWriteArrayListE implements ListE, RandomAccess, Cloneable, java.io.Serializable {...// ReentrantLock锁final transient ReentrantLock lock new ReentrantLock();// Object数组private transient volatile Object[] array;...
}构造函数
CopyOnWriteArrayList的构造函数
/*** 无参构造函数*/
public CopyOnWriteArrayList() {// 创建Object数组setArray(new Object[0]);
}
/*** 有参构造函数* c集合*/
public CopyOnWriteArrayList(Collection? extends E c) {Object[] elements;// 如果参数c为CopyOnWriteArrayList对象则将c中的数组直接赋值给elements数组if (c.getClass() CopyOnWriteArrayList.class)elements ((CopyOnWriteArrayList?)c).getArray();else {// 将参数c转换为数组并赋值给elements数组elements c.toArray();// c.toArray might (incorrectly) not return Object[] (see 6260652)// 如果数组c的类型不是Object[]则将数组c中的元素复制给Object数组再将Object数组赋值给elements数组if (elements.getClass() ! Object[].class)elements Arrays.copyOf(elements, elements.length, Object[].class);}// 将CopyOnWriteArrayList的array属性指向elements数组setArray(elements);
}
/*** 有参构造函数* toCopyIn数组*/
public CopyOnWriteArrayList(E[] toCopyIn) {// 将数组toCopyIn中的元素复制给Object数组再将Object数组赋值给CopyOnWriteArrayList的array数组setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}核心方法
添加元素-add方法
CopyOnWriteArrayList通过CopyOnWriteArrayList#add方法向其中添加元素。
添加元素执行流程如图所示 处理流程
1线程获得ReentrantLock锁拷贝原始数组array属性对应的数组生成原始数组副本数组长度为原始数组的长度1。2向原始数组副本中添加元素。3将CopyOnWriteArrayList中的array属性值替换为原始数组副本线程释放ReentrantLock锁。
CopyOnWriteArrayList#add(E e)方法源码解析
/*** 向数组中末尾位置添加元素* e待添加的元素*/
public boolean add(E e) {final ReentrantLock lock this.lock;// 加锁lock.lock();try {// 获取原始数组array属性对应的数组Object[] elements getArray();int len elements.length;// 拷贝原始数组生成原始数组副本数组长度为原始数组的长度1Object[] newElements Arrays.copyOf(elements, len 1);// 向原始数组副本中添加元素newElements[len] e;// 将CopyOnWriteArrayList中的array属性值替换为原始数组副本setArray(newElements);return true;} finally {// 释放锁lock.unlock();}
}CopyOnWriteArrayList#add(int index, E element)方法源码解析
/*** 向数组中指定位置添加元素* index数组下标* element待添加的元素*/
public void add(int index, E element) {final ReentrantLock lock this.lock;// 加锁lock.lock();try {// 获取原始数组Object[] elements getArray();int len elements.length;// 数组下标超过数组长度或数组下标小于0抛出数组越界异常if (index len || index 0)throw new IndexOutOfBoundsException(Index: index, Size: len);Object[] newElements;int numMoved len - index;// 在原始数组末尾插入元素if (numMoved 0)// 拷贝原始数组生成原始数组副本数组长度为原始数组的长度1newElements Arrays.copyOf(elements, len 1);else {// 在原始数组中间插入元素创建新数组数组长度为原始数组的长度1newElements new Object[len 1];// 拷贝原始数组中index下标之前的元素到新数组中System.arraycopy(elements, 0, newElements, 0, index);// 拷贝原始数组中index下标之后的元素到新数组中注意此部分元素从index位置开始向后移动一位System.arraycopy(elements, index, newElements, index 1,numMoved);}// 向原始数组副本中添加元素newElements[index] element;// 将CopyOnWriteArrayList中的array属性值替换为原始数组副本setArray(newElements);} finally {lock.unlock();}
}删除元素-remove方法
CopyOnWriteArrayList通过CopyOnWriteArrayList#remove方法删除指定位置的元素。
CopyOnWriteArrayList#remove方法源码解析
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)// 将CopyOnWriteArrayList的array属性重新指向删除指定位置元素之后的原始数组副本setArray(Arrays.copyOf(elements, len - 1));else {// 删除原始数组中间元素创建新数组数组长度为原始数组的长度-1Object[] newElements new Object[len - 1];// 拷贝原始数组中index下标前面的元素到新数组中System.arraycopy(elements, 0, newElements, 0, index);// 拷贝原始数组中index下标后面的元素到新数组中注意此部分元素从index1位置开始向前移动一位System.arraycopy(elements, index 1, newElements, index,numMoved);// 将CopyOnWriteArrayList中的array属性值替换为原始数组副本setArray(newElements);}return oldValue;} finally {// 释放锁lock.unlock();}
}更新元素-set方法
CopyOnWriteArrayList通过CopyOnWriteArrayList#set方法更新指定位置的元素。
CopyOnWriteArrayList#set方法源码解析
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 semanticssetArray(elements);}//返回旧值return oldValue;} finally {// 释放锁lock.unlock();}
}获取元素-get方法
CopyOnWriteArrayList通过CopyOnWriteArrayList#get方法获取指定位置的元素。
CopyOnWriteArrayList#get方法源码解析
/*** 获取指定位置的元素* index数组下标*/
public E get(int index) {// 返回array数组中指定下标对应的元素return get(getArray(), index);
}
private E get(Object[] a, int index) {return (E) a[index];
}遍历元素-iterator方法
CopyOnWriteArrayList可以通过CopyOnWriteArrayList#iterator方法进行遍历。在调用CopyOnWriteArrayList#iterator方法时会创建一个COWIterator迭代器创建COWIterator迭代器时传入的是array数组的快照并初始化一个游标通过游标遍历array数组快照中的所有元素。
CopyOnWriteArrayList#iterator方法源码解析
// 迭代方法
public IteratorE iterator() {// 通过COWIterator迭代器遍历array数组return new COWIteratorE(getArray(), 0);
}
// COWIterator迭代器
static final class COWIteratorE implements ListIteratorE {// 保存array数组快照private final Object[] snapshot;// 游标private int cursor;private COWIterator(Object[] elements, int initialCursor) {cursor initialCursor;snapshot elements;}...// 删除public void remove() {throw new UnsupportedOperationException();}// 修改public void set(E e) {throw new UnsupportedOperationException();}// 新增public void add(E e) {throw new UnsupportedOperationException();}...
}其中COWIterator迭代器遍历的是array数组快照因此对array数组快照进行增删改操作时会抛出 UnsupportedOperationException异常。
使用示例
/*** author 南秋同学* CopyOnWriteArrayList使用示例*/
Slf4j
public class CopyOnWriteArrayListExample {private static CopyOnWriteArrayListString list new CopyOnWriteArrayList();SneakyThrowspublic static void main(String[] args) {// 创建新增元素线程Thread thread1 new Thread(new Runnable() {SneakyThrowsOverridepublic void run() {for (int i 0; i 8; i) {list.add(element- i);log.info(添加元素 element-{}, i);Thread.sleep(100);}}});// 创建删除元素线程Thread thread2 new Thread(new Runnable() {SneakyThrowsOverridepublic void run() {for (int i 0; i 5; i) {if(list.size() 0){list.remove(0);log.info(删除元素----- element-{}, i);}Thread.sleep(200);}}});thread1.start();thread2.start();thread1.join();thread2.join();log.info(list中的最终元素{}, list);// 遍历元素for (String element: list){log.info(打印元素{}, element);}}
}执行结果
23:03:24.187 [Thread-0] - 添加元素 element-0
23:03:24.187 [Thread-1] - 删除元素----- element-0
23:03:24.296 [Thread-0] - 添加元素 element-1
23:03:24.397 [Thread-1] - 删除元素----- element-1
23:03:24.397 [Thread-0] - 添加元素 element-2
23:03:24.498 [Thread-0] - 添加元素 element-3
23:03:24.600 [Thread-1] - 删除元素----- element-2
23:03:24.600 [Thread-0] - 添加元素 element-4
23:03:24.703 [Thread-0] - 添加元素 element-5
23:03:24.805 [Thread-1] - 删除元素----- element-3
23:03:24.807 [Thread-0] - 添加元素 element-6
23:03:24.909 [Thread-0] - 添加元素 element-7
23:03:25.007 [Thread-1] - 删除元素----- element-4
23:03:25.211 [main] - list中的最终元素[element-5, element-6, element-7]
23:03:25.212 [main] - 打印元素element-5
23:03:25.212 [main] - 打印元素element-6
23:03:25.212 [main] - 打印元素element-7从执行结果可以看出多个线程同时对CopyOnWriteArrayList进行写操作时可以保证线程安全。