前言
CopyOnWriteArrayList是一個線程安全的ArrayList,對ArrayList內部結構不太清楚的可以看看博主的這篇文章:從源碼分析java容器之ArrayList,看完這篇文章,你會知道爲什麼ArrayList是非線程安全的。然後你再來看這篇文章,看看CopyOnWriteArrayList是如何保證線程安全的。
1、結構圖
從結構圖中,我們可以看出CopyOnWriteArrayList本質上還是一個List,內部數據結構還是數組。
2、分析源碼
2.1、屬性
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
private static final long serialVersionUID = 8673264195747942595L;
//增加了一個ReentrantLock互斥鎖
//我們應該能夠猜到是保證增刪的線程安全,保證了寫的線程安全
final transient ReentrantLock lock = new ReentrantLock();
//內部數組增加了volatile修飾
//增加了數組多線程中在內存的可見性,保證了讀的線程安全
private transient volatile Object[] array;
//以下省略……
}
2.2、構造方法
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
public CopyOnWriteArrayList(Collection<? extends E> c) {
Object[] elements = c.toArray();
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));
}
2.3、add()方法
public boolean add(E e) {
final ReentrantLock lock = this.lock;
//獲取該對象的獨佔鎖(在同一個時間點只能被一個線程鎖持有),保證線程安全
lock.lock();
try {
//獲取內部數組元素
Object[] elements = getArray();
int len = elements.length;
//創建一個比舊數據長度+1的新數組
//複製舊數組數據到新數組
Object[] newElements = Arrays.copyOf(elements, len + 1);
//將添加的元素放置到數組尾部
newElements[len] = e;
//新數組指向Volatile修飾的array
setArray(newElements);
return true;
} finally {
//釋放鎖
lock.unlock();
}
}
上面的add()方法步驟是:
- 先copy舊數組到新數組
- 然後寫入新增的元素
- 再將新數組指向Volatile修飾的array數組
有木有突然明白爲什麼它叫CopyOnWriteArrayList。
3、總結
- CopyOnWriteArrayList內部數據結構是數組。
- CopyOnWriteArrayList內部有一個ReentrantLock對象的成員變量lock,lock保證了寫和刪除操作的線程安全。
- CopyOnWriteArrayList內部的數組是用Volatile修飾,保證了讀操作的線程安全。
- CopyOnWriteArrayList的擴容是每次+1,適合的場景是讀多寫少的場景。
結束語
通過本篇的分析,我們知道了CopyOnWriteArrayList相比ArrayList,如何保證了線程安全。
下一篇,我們將分析CopyOnWriteArraySet的實現。