【多線程】JUC版的CopyOnWriteArrayList
CopyOnWriteArrayList
- CopyOnWriteArrayList適合于多線程場景下使用,其采用讀寫分離的思想,讀操作不上鎖,寫操作上鎖,且寫操作效率較低。
- CopyOnWriteArrayList基于fail-safe機制,每次修改都會在原先基礎上復制一份,修改完畢后在進行替換。
- CopyOnWriteArrayList采用的是ReentrantLock進行上鎖。
- CopyOnWriteArrayList和ArrayList一樣,其底層數據結構也是數組,加上transient不讓其被序列化,加上volatile修飾來保證多線程下的其可見性和有序性。
- CopyOnWriteArrayList效率比ArrayList低不少,畢竟多線程場景下,其每次都是要在原數組基礎上復制一份在操作耗內存和時間,而ArrayList只是容量滿了進行擴容,因此在非多線程的場景下還是用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方法一致。

浙公網安備 33010602011771號