Java中不同容器類是否線程安全


非線程安全 線程安全
ArrayList
LinkedList
Vector
HashMap HashTable
StringBuilder StringBuffer

區別

容器類線程安全, 非線程安全的區別可以用下面這個例子來表述:

ArrayListVector為例, 同時建立100個線程, 每個線程都向容器中添加100個元素,
最后統計容器內元素的數量, 對於ArrayList來說, 最后的量並不一定是10000個, 甚至會出現IndexOutofBoundsException, 但是對於Vector來說, 最后的量一定是10000個, 且不會出現任何異常. 這便是線程安全與非線程安全的一個直觀表現.

非線程安全 != 多線程下不可使用

非線程安全並不意味着不可以在多線程環境下不可使用, 上述問題出現在多個線程操作同一個ArrayList對象, 如果一個ArrayList只在一個線程下進行操作, 還是可以使用ArrayList的.

如何使非線程安全容器類變得線程安全

使用List<Object> list = Collections.synchronizedList(new ArrayList<Object>());可以使list變得線程安全.

造成非線程安全的原因

一般來說, 造成非線程安全主要有兩個原因:
1. 一個操作不是原子性操作
2. 執行過程中可能被中斷

查看ArrayList關於add(E e)的相關源碼:

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

list中含有null的原因

即使不發生IndexOutofBoundsException異常, 最后的元素總數也不全都是100000個.
問題出現在add(E e)中的elementData[size++] = e;, 這句代碼大致會分成以下兩步:

  1. elementData[size] = e;
  2. `size++“

如果線程A執行完第1步中斷, 線程B開執行add, 執行到第1步時候因為size還未+1, 所以線程B仍會將e賦值給elementData[size], 之后線程B執行+1操作, 線程A也執行+1操作, 也就意味着,並沒有對 elementData[size+1]進行賦值, 其值也就為null.

元素總量不符合預期的原因

根本原因在於自加操作不是原子性的

線程B可能在線程A執行size++中間就開始同時執行size++, 這可能會使得線程A,B執行之初時size值相同, 導致元素總量小於預期.

IndexOutofBoundsException產生原因

ArrayList實際上也是一個數組, 只不過可以自動擴容, 出現IndexOutofBoundsException說明在某些情況下, 還未擴容, 就添加元素進去了.

例如,線程A開始執行add(), 執行到ensureExplicitCapacity(int minCapacity)中的條件語句時, 如果此時添加的元素總數==數組的長度-1, 那么並不會執行擴容操作. 但是如果此時, 線程A中斷, 線程B開始執行, 此時由於線程A還未添加元素, 元素總數仍==數組的長度-1, 添加元素, 此時若線程A恢復, 開始執行添加元素, 由於此時元素總數==數組的長度, 再向其中添加元素就會拋出IndexOutofBoundsException異常.

Vector

Vector中關於add(E e)的相關源碼

public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM