ArrayList 為什麼執行緒不安全

weixin_34391445發表於2018-02-28

我們先來看看 ArrayList 的 add 操作原始碼。

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

ArrayList 的不安全主要體現在兩個方面。

  • 其一:
elementData[size++] = e;

不是一個原子操作,是分兩步執行的。

elementData[size] = e;
size++;

單執行緒執行這段程式碼完全沒問題,可是到多執行緒環境下可能就有問題了。可能一個執行緒會覆蓋另一個執行緒的值。

  1. 列表為空 size = 0。
  2. 執行緒 A 執行完 elementData[size] = e;之後掛起。A 把 "a" 放在了下標為 0 的位置。此時 size = 0。
  3. 執行緒 B 執行 elementData[size] = e; 因為此時 size = 0,所以 B 把 "b" 放在了下標為 0 的位置,於是剛好把 A 的資料給覆蓋掉了。
  4. 執行緒 B 將 size 的值增加為 1。
  5. 執行緒 A 將 size 的值增加為 2。

這樣子,當執行緒 A 和執行緒 B 都執行完之後理想情況下應該是 "a" 在下標為 0 的位置,"b" 在標為 1 的位置。而實際情況確是下標為 0 的位置為 "b",下標為 1 的位置啥也沒有。

  • 其二:

ArrayList 預設陣列大小為 10。假設現在已經新增進去 9 個元素了,size = 9。

  1. 執行緒 A 執行完 add 函式中的ensureCapacityInternal(size + 1)掛起了。
  2. 執行緒 B 開始執行,校驗陣列容量發現不需要擴容。於是把 "b" 放在了下標為 9 的位置,且 size 自增 1。此時 size = 10。
  3. 執行緒 A 接著執行,嘗試把 "a" 放在下標為 10 的位置,因為 size = 10。但因為陣列還沒有擴容,最大的下標才為 9,所以會丟擲陣列越界異常 ArrayIndexOutOfBoundsException

相關文章