Java中的拷貝數(shù)組CopyOnWriteArrayList詳解
CopyOnWriteArrayList詳解
ArrayList和LinkedList都不是線程安全的,如果需要線程安全的List,可以使用Collections.synchronizedList來生成一個同步list,但是這個同步list的方法都是通過synchronized修飾來保證同步的,并發(fā)性能不高。那么如何提高并發(fā)性能呢?比如某些場景下,對List的讀操作遠多于寫操作,那么CopyOnWriteArrayList就派上用場了。
1、屬性
/** The lock protecting all mutators */ final transient ReentrantLock lock = new ReentrantLock(); /** The array, accessed only via getArray/setArray. */ private transient volatile Object[] array;
lock是用來在寫操作時加鎖使用的,具體使用下面方法部分再看。
數(shù)組array是CopyOnWriteArrayList的核心部分了,所有的元素都存放在這個數(shù)組中。為了保證可見性,所以被volatile修飾著。
2、方法
final Object[] getArray() {
return array;
}
final void setArray(Object[] a) {
array = a;
}
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
public CopyOnWriteArrayList(Collection<? extends E> c) {
Object[] elements;
if (c.getClass() == CopyOnWriteArrayList.class)
elements = ((CopyOnWriteArrayList<?>)c).getArray();
else {
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));
}三個構(gòu)造函數(shù)都是為了給array數(shù)組賦值,生成初始數(shù)組。
下面看下它的幾個關(guān)鍵方法:get、add、remove
private E get(Object[] a, int index) {
return (E) a[index];
}
/**
* {@inheritDoc}
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
return get(getArray(), index);
}get比較簡單,就是直接取數(shù)組索引處的元素。注意get方法沒有加鎖。
public boolean add(E e) {
final ReentrantLock lock = this.lock;
//1、加鎖
lock.lock();
try {
//2、取原數(shù)組
Object[] elements = getArray();
int len = elements.length;
//3、拷貝生成新數(shù)組
Object[] newElements = Arrays.copyOf(elements, len + 1);
//4、新元素加到數(shù)組最后一位
newElements[len] = e;
//5、數(shù)組替換
setArray(newElements);
return true;
} finally {
//6、釋放鎖
lock.unlock();
}
}add方法,上來就先加鎖,然后取出原數(shù)組后拷貝生成了一個新的數(shù)組,注意,此時原有的array數(shù)組沒有變,get訪問時還是跟之前一樣。當(dāng)把新的數(shù)組替換掉array后,由于是volatile修飾的,get訪問時就會訪問添加過元素的新數(shù)組。這樣就保證了讀寫同時進行時,讀不需要加鎖依然不會有并發(fā)問題。最后釋放鎖后,別的寫操作獲得鎖,再次進行替換操作,這樣保證寫操作與寫操作之間不會有并發(fā)問題。
public E remove(int index) {
final ReentrantLock lock = this.lock;
//1、獲取鎖
lock.lock();
try {
//2、取原數(shù)組
Object[] elements = getArray();
int len = elements.length;
E oldValue = get(elements, index);
int numMoved = len - index - 1;
//3、如果被刪除元素是數(shù)組最后一位,直接截取len-1的新數(shù)組
if (numMoved == 0)
setArray(Arrays.copyOf(elements, len - 1));
//4、否則,分段拷貝生成新數(shù)組
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;
} finally {
//5、釋放鎖
lock.unlock();
}
}remove與add同理,也是先獲取鎖,同時生成新的數(shù)組之后再把原有的數(shù)組進行替換。
3、總結(jié)
- 所有的元素均存儲到數(shù)組中
- 寫操作(add/set/remove等)會改變數(shù)組結(jié)構(gòu),采用新生成一個數(shù)組然后進行替換的做法,保證了在此期間原數(shù)組可以正常訪問。同時操作之前需要先獲取鎖,避免寫操作之間產(chǎn)生并發(fā)問題。
- 讀操作由于不需要改變數(shù)組結(jié)構(gòu),且寫操作時,對原有的數(shù)組不進行修改,此時仍可正常讀取。寫操作將新數(shù)組進行替換后,由于數(shù)組被volatile修飾,保證了可見性,此時也可正正常讀取。所以讀操作不需要加鎖。
- 因為每次寫操作都會帶來數(shù)組拷貝,所以當(dāng)讀操作遠大于寫操作時,才可考慮使用此容器。
到此這篇關(guān)于Java中的拷貝數(shù)組CopyOnWriteArrayList詳解的文章就介紹到這了,更多相關(guān)CopyOnWriteArrayList詳解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
idea中增強for循環(huán)提示unexpected token問題
這篇文章主要介紹了idea中增強for循環(huán)提示unexpected token問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
Spring?AOT優(yōu)化轉(zhuǎn)換的使用原理詳解
這篇文章主要介紹了Spring?AOT優(yōu)化轉(zhuǎn)換的使用原理,Spring?AOT是一種在構(gòu)建時對Spring應(yīng)用進行優(yōu)化的技術(shù),主要為GraalVM原生鏡像生成配置,同時提升傳統(tǒng)JVM的啟動性能,需要的朋友可以參考下2025-10-10
springboot 默認(rèn)靜態(tài)路徑實例解析
這篇文章主要介紹了springboot 默認(rèn)靜態(tài)路徑實例解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-11-11
Spring注解秘籍之如何優(yōu)雅地使用@RequestHeader
@RequestHeader注解是Spring MVC中一個強大的工具,它為處理HTTP請求頭提供了一種簡潔而直觀的方法,這篇文章主要介紹了Spring注解秘籍之如何優(yōu)雅地使用@RequestHeader的相關(guān)資料,需要的朋友可以參考下2026-04-04

