CopyOnWriteArrayList是什麼
- CopyOnWriteArrayList是List接口的同步實作
- CopyOnWriteArrayList是基于數組複制的操作,對于增、删、改的操作開銷很大
- CopyOnWriteArrayList使用疊代器周遊不會導緻與其他線程發生沖突,依賴于不變的數組快照
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {}
- CopyOnWriteArrayList實作了List接口、底層使用數組儲存所有元素,其操作基本上是對數組的操作
- CopyOnWriteArrayList實作了RandmoAccess接口,即提供了随機通路功能,RandmoAccess是java中用來被List實作,為List提供快速通路功能的,我們可以通過元素的序号快速擷取元素對象,這就是快速随機通路
- CopyOnWriteArrayList實作了Cloneable接口,即覆寫了函數clone(),能被克隆
- CopyOnWriteArrayList實作了java.io.Serializable接口,意味着ArrayList支援序列化
CopyOnWriteArrayList成員變量
//用于寫操作的鎖
final transient Object lock = new Object();
//用于儲存數組的數組,volatile用于線程的可見性
private transient volatile Object[] elements;
CopyOnWriteArrayList構造方法
CopyOnWriteArrayList一共提供了3個構造方法
- public CopyOnWriteArrayList(E[] toCopyIn):用指定的數組,複制到目前清單中
- public CopyOnWriteArrayList(Collection<? extends E> c):用指定的清單,複制到目前清單中
- public CopyOnWriteArrayList():預設構造函數,建立一個空清單
CopyOnWriteArrayList的存儲
public boolean add(E e) {
//1、先加鎖
synchronized (lock) {
Object[] elements = getArray();
int len = elements.length;
//2、拷貝數組,長度為原容器長度加一
Object[] newElements = Arrays.copyOf(elements, len + 1);
//3、将元素加入到新數組中
newElements[len] = e;
//4、将array引用指向到新數組
setArray(newElements);
return true;
}
}
存儲邏輯主要是将原資料拷貝一份新副本,在新副本上做添加操作,最後再将引用切換到新副本上
CopyOnWriteArrayList的删除
public E remove(int index) {
//1、先加鎖
synchronized (lock) {
//2、擷取目前數組
Object[] elements = getArray();
int len = elements.length;
//3、擷取移除的元素
E oldValue = get(elements, index);
int numMoved = len - index - 1;
if (numMoved == 0)
//4、如果屬于最後一個元素,則拷貝len-1的元素到新副本上,并切換引用
setArray(Arrays.copyOf(elements, len - 1));
else {
//5、如果不是,則将删除元素之外的其他元素拷貝到新副本中,并切換引用
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;
}
}
删除邏輯主要是數組的操作,同存儲邏輯差不多,都是拷貝新副本後再切換引用的操作
CopyOnWriteArrayList的擷取
public E get(int index) {
return get(getArray(), index);
}
private E get(Object[] a, int index) {
return (E) a[index];
}
final Object[] getArray() {
return elements;
}
擷取的邏輯主要是數組的快速通路,性能很高,在高并發的情況下,無法保證其他線程對副本的操作後再切換給原資料
在多線程并發的情況下,get操作還是會擷取到髒資料
CopyOnWriteArrayList優缺點
1、優點
- 讀操作性能很高,無需同步操作,比較适用于讀多寫少的并發場景
- 在疊代器周遊的時候,保證線程是安全的,不會抛出線程不安全的錯誤異常
2、缺點
- 記憶體占用問題,由于寫操作的時候,需要拷貝數組,會消耗記憶體,在原數組較多的情況下,容易導緻gc操作
- 無法保證明時性,不能用于實時讀的場景,由于get操作是擷取原資料,無法保證其他線程對副本的操作後再切換給原資料
CopyOnWriteArrayList總結
- 讀寫分離,适用于讀多寫少的場景
- 用拷貝副本的思想來解決并發沖突
- 無法保證明時性