天天看點

JDK源碼解析之ArrayList與Vector與CopyOnWriteArrayList

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線程安全,性能高同時支援并發操作

繼續閱讀