1. ArrayList的線程安全問題:
public static void main(String[] args) {
final List<Integer> list = new ArrayList<Integer>();
for (int i=0;i<5000;i++) {
list.add(i);
}
//開始5個線程,每個線程添加1000個資料
for(int i=0;i<5;i++) {
new Thread(new Runnable() {
@Override
public void run() {
for(int j=0;j<1000;j++){
list.add(j);
}
}
}).start();
}
//開啟5個線程,每個線程删除1000個資料
for (int i=0;i<5;i++){
new Thread(new Runnable() {
@Override
public void run() {
for(int j=0;j<1000;j++){
list.remove(0);
}
}
}).start();
}
try {
Thread.sleep(10000);
} catch (InterruptedException e) {}
System.out.printf("list size="+list.size());
}
運作發現結果不是5000,每次運作的結果可能都不一樣,如果換成vector呢結果就會得到想要的5000,換成CopyOnWriteArrayList也可以得到想要結果,既然Vector和CopyOnWriteArrayList都是線程安全的,那他兩有什麼不同呢
2. Vector的實作:
a. vector的方法是synchronized的,也就是線程安全的,這也會導緻其性能比ArrayList低
b. Vector擴容比ArrayList大,每次擴容為1倍而ArrayList為0.5倍,這就說明當初始化容量不足時,ArrayList更節省空間,源碼如下:
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
private void ensureCapacityHelper(int minCapacity) {
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object[] oldData = elementData;
int newCapacity = (capacityIncrement > 0) ?
(oldCapacity + capacityIncrement) : (oldCapacity * 2);
if (newCapacity < minCapacity) {
newCapacity = minCapacity;
}
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
c. Vector和ArrayList一樣不支援讀寫并發操作,否則會抛出并發異常ConcurrentModificationException,這個需要注意的
3. CopyOnWriteArrayList的實作:
a. 使用ReentrantLock鎖保證線程安全,CopyOnWriteArrayList新增時會建立一個數組添加,添加完成後再将原數組指向新的數組,這就導緻記憶體占用問題,如果對象比較大可能會導緻頻繁發生Yong GC和Full GC,使用時需要注意記憶體問題
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
final void setArray(Object[] a) {
array = a;
}
b. CopyOnWriteArrayList的這種讀寫分離機制沒有鎖定原數組,也就是可以同時讀取原數組資料,不過讀取的是修改前的舊資料,達到了讀寫并發要求
public E get(int index) {
return (E)(getArray()[index]);
}
final Object[] getArray() {
return array;
}
c . CopyOnWriteArrayList能保證資料的最終一緻性,但不能保證資料的實時一緻性,這個需注意
4. 總結
a. ArrayList是線程不安全的
b. Vector線程安全,但性能低
c. CopyOnWriteArrayList線程安全,性能高同時支援并發操作