非線程安全 | 線程安全 |
---|---|
ArrayList LinkedList |
Vector |
HashMap |
HashTable |
StringBuilder |
StringBuffer |
容器類線程安全, 非線程安全的區別能夠用下面這個例子來表述:java
以ArrayList
和Vector
爲例, 同時創建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);
}
即便不發生IndexOutofBoundsException
異常, 最後的元素總數也不全都是100000個.
問題出如今add(E e)
中的elementData[size++] = e;
, 這句代碼大體會分紅如下兩步:this
elementData[size] = e;
若是線程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
ArrayList實際上也是一個數組, 只不過能夠自動擴容, 出現IndexOutofBoundsException
說明在某些狀況下, 還未擴容, 就添加元素進去了.
例如,線程A開始執行add()
, 執行到ensureExplicitCapacity(int minCapacity)
中的條件語句時, 若是此時添加的元素總數==數組的長度-1, 那麼並不會執行擴容操做. 可是若是此時, 線程A中斷, 線程B開始執行, 此時因爲線程A還未添加元素, 元素總數仍==數組的長度-1, 添加元素, 此時若線程A恢復, 開始執行添加元素, 因爲此時元素總數==數組的長度, 再向其中添加元素就會拋出IndexOutofBoundsException
異常.
Vector
中關於add(E e)
的相關源碼
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}