我們先來看看 ArrayList 的 add 操作源碼。
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
ArrayList 的不安全主要體現在兩個方面。
- 其一
elementData[size++] = e;
不是一個原子操作,是分兩步執行的。
elementData[size] = e;
size++;
單線程執行這段代碼完全沒問題,可是到多線程環境下可能就有問題了。可能一個線程會覆蓋另一個線程的值。
- 列表爲空 size = 0。
- 線程 A 執行完
elementData[size] = e;
之後掛起。A 把 "a" 放在了下標爲 0 的位置。此時 size = 0。 - 線程 B 執行
elementData[size] = e;
因爲此時 size = 0,所以 B 把 "b" 放在了下標爲 0 的位置,於是剛好把 A 的數據給覆蓋掉了。 - 線程 B 將 size 的值增加爲 1。
- 線程 A 將 size 的值增加爲 2。
- 這樣子,當線程 A 和線程 B 都執行完之後理想情況下應該是 "a" 在下標爲 0 的位置,"b" 在標爲 1 的位置。而實際情況確是下標爲 0 的位置爲 "b",下標爲 1 的位置啥也沒有。
- 其二
ArrayList 默認數組大小爲 10。假設現在已經添加進去 9 個元素了,size = 9。
- 線程 A 執行完 add 函數中的
ensureCapacityInternal(size + 1)
掛起了。 - 線程 B 開始執行,校驗數組容量發現不需要擴容。於是把 "b" 放在了下標爲 9 的位置,且 size 自增 1。此時 size = 10。
-
線程 A 接着執行,嘗試把 "a" 放在下標爲 10 的位置,因爲 size = 10。但因爲數組還沒有擴容,最大的下標才爲 9,所以會拋出數組越界異常
ArrayIndexOutOfBoundsException