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

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

區別

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

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

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

非線程安全並不意味着不能夠在多線程環境下不可以使用, 上述問題出如今多個線程操做同一個ArrayList對象, 若是一個ArrayList只在一個線程下進行操做, 仍是可使用ArrayList的.安全

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

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

形成非線程安全的緣由

通常來講, 形成非線程安全主要有兩個緣由:
1. 一個操做不是原子性操做
2. 執行過程當中可能被中斷多線程

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

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;, 這句代碼大體會分紅如下兩步:this

  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.spa

元素總量不符合預期的緣由

根本緣由在於自加操做不是原子性的線程

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

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;
}
相關文章
相關標籤/搜索