? ? 今天來(lái)說(shuō)一說(shuō)CopyOnWriteArrayList類咸这,縱觀JUC包下并發(fā)List只有CopyOnWriteArrayList類。CopyOnWriteArrayList是一個(gè)線程安全的ArrayList酿雪,從它的命名也能看出在進(jìn)行寫操作的時(shí)候會(huì)進(jìn)行復(fù)制侄刽,這種寫時(shí)復(fù)制策略會(huì)產(chǎn)生弱一致性問(wèn)題。下面對(duì)CopyOnWriteArrayList類繼續(xù)剖析袋励。
? ? 在CopyOnWriteArrayList類中都是圍繞著以下兩個(gè)成員變量進(jìn)行操作的:
final transient ReentrantLock lock =new ReentrantLock();
private transient volatile Object[] array;
lock獨(dú)占鎖對(duì)象,保證同時(shí)只能有一個(gè)線程對(duì)array數(shù)據(jù)進(jìn)行寫操作茬故。關(guān)于ReentrantLock獨(dú)占鎖的結(jié)束點(diǎn)這里磺芭。
array數(shù)組用來(lái)存放具體的元素,用關(guān)鍵字volatile修飾保證了內(nèi)存的可見性钾腺。
? ? 下面對(duì)CopyOnWriteArrayList常用方法進(jìn)行源碼剖析放棒。
add(E var1)
? ? 首當(dāng)其沖的當(dāng)然是添加操作,CopyOnWriteArrayList的add方法使用ReentrantLock保證同一時(shí)間只有一個(gè)線程對(duì)array進(jìn)行操作间螟,在進(jìn)行添加元素會(huì)先創(chuàng)建一個(gè)新數(shù)組,拷貝array數(shù)組中的所有元素荣瑟,并將新增元素添加到新數(shù)組之后再?gòu)?fù)制給array數(shù)組摩泪。由于每次新增都會(huì)創(chuàng)建一個(gè)新數(shù)組并且長(zhǎng)度是原數(shù)組長(zhǎng)度加1,所有CopyOnWriteArrayList是無(wú)界的嚷掠。
public boolean add(E var1) {
//(1)
ReentrantLock var2 =this.lock;
? ? var2.lock();
? ? boolean var6;
? ? try {
Object[] var3 =this.getArray();
? ? ? ? int var4 = var3.length;
//(2)
? ? ? ? Object[] var5 = Arrays.copyOf(var3, var4 +1);
? ? ? ? var5[var4] = var1;
//(3)
? ? ? ? this.setArray(var5);
? ? ? ? var6 =true;
? ? }finally {
//(4)
var2.unlock();
? ? }
return var6;
}
(1)獲取獨(dú)占鎖對(duì)象叠国,保證只有一個(gè)線程進(jìn)行array數(shù)組的寫操作戴尸。
(2)創(chuàng)建一個(gè)新數(shù)組,長(zhǎng)度是array數(shù)組長(zhǎng)度加1孙蒙,并將array數(shù)組中的元素拷貝到新數(shù)組中。將新數(shù)組的最后一個(gè)元素賦值為需要增加的元素香追。
(3)將新數(shù)組賦值個(gè)array數(shù)組坦胶。
(4)釋放獨(dú)占鎖對(duì)象晴楔,注意:在開發(fā)使用中也需要向上述源碼一樣在finally中進(jìn)行鎖的釋放税弃,確保即使發(fā)生異常也對(duì)鎖進(jìn)行釋放凑队。
remove(int var1)
? ? 由于remove(Object var1) 、remove(Object var1, Object[] var2, int var3)方法原理都是一樣的漩氨,所以下面就對(duì)remove(int var1)源碼進(jìn)行剖析叫惊。
? ??刪除array數(shù)組中指定下標(biāo)的元素,首先獲取獨(dú)占鎖赋访,保證同一時(shí)間只有一個(gè)線程對(duì)array數(shù)據(jù)進(jìn)行寫操作,然后獲取數(shù)組中要被刪除的元素,并把剩余的元素復(fù)制到一個(gè)新數(shù)組步悠,將新數(shù)組賦值為array數(shù)組瘫镇,最后釋放鎖。
public E remove(int var1) {
//(1)
ReentrantLock var2 =this.lock;
? ? var2.lock();
? ? Object var11;
? ? try {
Object[] var3 =this.getArray();
? ? ? ? int var4 = var3.length;
//(2)
? ? ? ? Object var5 =this.get(var3, var1);
? ? ? ? int var6 = var4 - var1 -1;
//(3)
? ? ? ? if (var6 ==0) {
this.setArray(Arrays.copyOf(var3, var4 -1));
? ? ? ? }else {
Object[] var7 =new Object[var4 -1];
? ? ? ? ? ? System.arraycopy(var3, 0, var7, 0, var1);
? ? ? ? ? ? System.arraycopy(var3, var1 +1, var7, var1, var6);
? ? ? ? ? ? this.setArray(var7);
? ? ? ? }
var11 = var5;
? ? }finally {
//(4)
var2.unlock();
? ? }
return var11;
}
(1)獲取獨(dú)占鎖谚咬,保證只有一個(gè)線程進(jìn)行寫操作尚粘。
(2)獲取指定下標(biāo)的元素。
(3)判斷要?jiǎng)h除的是否為最后一個(gè)元素秉继,把除需要?jiǎng)h除的元素之外的元素拷貝到一個(gè)新數(shù)組泽铛,并將新數(shù)組賦值給array數(shù)組。
(4)最終釋放獨(dú)占鎖杠茬。?
get(int var1)
? ? 獲取指定下標(biāo)的元素,該方法并沒(méi)有進(jìn)行加鎖同步瓢喉,所以在獲取元素的時(shí)候可能該元素已經(jīng)被其他線程刪除了灯荧,這就是CopyOnWriteArrayList類中寫時(shí)復(fù)制策略產(chǎn)生的弱一致性問(wèn)題。
public E get(int var1) {
return this.get(this.getArray(), var1);
}
final Object[] getArray() {
return this.array;
}
private E get(Object[] var1, int var2) {
return var1[var2];
}
? ? 從上面的源碼介紹中可以知道逗载,對(duì)CopyOnWriteArrayList類的寫操作都會(huì)創(chuàng)建一個(gè)新的數(shù)組并對(duì)原數(shù)組的元素進(jìn)行拷貝靖榕,當(dāng)在寫頻繁并元素多的情況下CopyOnWriteArrayList的性能可想而知形葬,并且還有一致性的問(wèn)題码荔,所以在實(shí)現(xiàn)開發(fā)中并不會(huì)怎么用到CopyOnWriteArrayList類感挥。
????今天的分享就到這,有看不明白的地方一定是我寫的不夠清楚硼瓣,所有歡迎提任何問(wèn)題以及改善方法置谦。