寫入時複製是一種計算機程序設計領域的優化策略。其核心思想是,若是有多個調用者同時請求相同資源(如內存或磁盤上的數據存儲),他們會共同獲取相同的指針指向相同的資源,直到某個調用者試圖修改資源的內容時,系統纔會真正複製一份專用副本)給該調用者,而其餘調用者所見到的最初的資源仍然保持不變。數組
集合數據主要就兩個操做讀、寫;讀是讀取內容,寫是改變內容;讀的時候讀取存儲資源,寫的時候,複製一份修改後再重置原有資源;這樣就具備如下特點:安全
所以若是要作到線程安全,就必須再寫時進行加鎖bash
而CopyOnWriteArrayList集合就是這種寫時複製+寫時加鎖來實現的;CopyOnWriteArraySet內部代理了CopyOnWriteArrayList,進而實現不重複數據的集合;下面就來介紹下CopyOnWriteArrayList併發
list集合,內部採用數組來存儲;寫時複製,並加鎖;直接讀數據;工具
final transient Object lock = new Object();
private transient volatile Object[] array;
複製代碼
lock是對synchronized鎖標誌,array是資源數組,使用volatile關鍵字,寫操做會再任何線程下次操做資源時可見性能
public boolean add(E e) {
synchronized (lock) {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
}
}
複製代碼
public void add(int index, E element) {
synchronized (lock) {
Object[] elements = getArray();
int len = elements.length;
if (index > len || index < 0)
throw new IndexOutOfBoundsException(outOfBounds(index, len));
Object[] newElements;
int numMoved = len - index;
if (numMoved == 0)
newElements = Arrays.copyOf(elements, len + 1);
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);
}
}
複製代碼
特定索引增長數據,能夠任意位置增長數據;優化
保證數據不重複,增長數據ui
public boolean addIfAbsent(E e) {
Object[] snapshot = getArray();
return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
addIfAbsent(e, snapshot);
}
private boolean addIfAbsent(E e, Object[] snapshot) {
synchronized (lock) {
Object[] current = getArray();
int len = current.length;
if (snapshot != current) {
// Optimize for lost race to another addXXX operation
int common = Math.min(snapshot.length, len);
for (int i = 0; i < common; i++)
if (current[i] != snapshot[i]
&& Objects.equals(e, current[i]))
return false;
if (indexOf(e, current, common, len) >= 0)
return false;
}
Object[] newElements = Arrays.copyOf(current, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
}
}
複製代碼
public E set(int index, E element) {
synchronized (lock) {
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;
}
}
複製代碼
改變指定位置數據:spa
public E remove(int index) {
synchronized (lock) {
Object[] elements = getArray();
int len = elements.length;
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;
}
}
複製代碼
加鎖,進行copy數據,copy數據時,不包括要刪除的數據,把copy數據重置回去線程
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) {
synchronized (lock) {
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]
&& Objects.equals(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;
}
}
複製代碼
public void clear() {
synchronized (lock) {
setArray(new Object[0]);
}
}
複製代碼
加鎖,置爲空數組
很簡單的邏輯,獲取數據索引位置
public E get(int index) {
return get(getArray(), index);
}
private E get(Object[] a, int index) {
return (E) a[index];
}
複製代碼
還有一些其它方法,好比獲取位置索引,集合大小等
就不介紹了,它內部使用了CopyOnWriteArraySet來代理功能;而且減小了方法
技術變化都很快,但基礎技術、理論知識永遠都是那些;做者但願在餘後的生活中,對經常使用技術點進行基礎知識分享;若是你以爲文章寫的不錯,請給與關注和點贊;若是文章存在錯誤,也請多多指教!