【多執行緒】JUC版的CopyOnWriteArrayList
阿新 • • 發佈:2022-04-01
CopyOnWriteArrayList
- CopyOnWriteArrayList適合於多執行緒場景下使用,其採用讀寫分離的思想,讀操作不上鎖,寫操作上鎖,且寫操作效率較低。
- CopyOnWriteArrayList基於fail-safe機制,每次修改都會在原先基礎上複製一份,修改完畢後在進行替換。
- CopyOnWriteArrayList採用的是ReentrantLock進行上鎖。
- CopyOnWriteArrayList和ArrayList一樣,其底層資料結構也是陣列,加上transient不讓其被序列化,加上volatile修飾來保證多執行緒下的其可見性和有序性。
- CopyOnWriteArrayList效率比ArrayList低
建構函式
public CopyOnWriteArrayList() { //預設建立一個大小為0的陣列 setArray(new Object[0]); } final void setArray(Object[] a) { array = a; } public CopyOnWriteArrayList(Collection<? extends E> c) { Object[] elements; //如果當前集合是CopyOnWriteArrayList的型別的話,直接賦值給它 if (c.getClass() == CopyOnWriteArrayList.class) elements = ((CopyOnWriteArrayList<?>)c).getArray(); else { //否則呼叫toArra()將其轉為陣列 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); } public CopyOnWriteArrayList(E[] toCopyIn) { //將傳進來的陣列元素拷貝給當前陣列 setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class)); }
讀取方法(未加鎖)
final Object[] getArray() { return array; } public int size() { return getArray().length; } public boolean isEmpty() { return size() == 0; } public int indexOf(E e, int index) { Object[] elements = getArray(); return indexOf(e, elements, index, elements.length); } public int lastIndexOf(Object o) { Object[] elements = getArray(); return lastIndexOf(o, elements, elements.length - 1); } ........
add方法(使用ReentrantLock加鎖)
public boolean add(E e) {
//使用ReentrantLock上鎖
final ReentrantLock lock = this.lock;
lock.lock();
try {
//呼叫getArray()獲取原來的陣列
Object[] elements = getArray();
int len = elements.length;
//複製老陣列,得到一個長度+1的陣列
Object[] newElements = Arrays.copyOf(elements, len + 1);
//新增元素,在用setArray()函式替換原陣列
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
可見其修改操作是基於fail-safe機制,像我們的String一樣,不在原來的物件上直接進行操作,而是複製一份對其進行修改,另外此處的修改操作是利用Lock鎖進行上鎖的,所以保證了執行緒安全問題。
remove方法(使用ReentrantLock加鎖)
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();
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();
}
}
思路與add方法一致。