ArrayListがスレッドセーフではない理由

[ArrayListの実装]

ArrayListの実装には、主に次のものが含まれます。ObjectelementData配列はすべての要素を保持し、size変数は現在の配列に追加された要素の数を保持します。

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;

【追加操作のソースコード】

追加操作は、大きく2つのステップに分けられます。1。リストの容量が十分であるかどうか、および拡張が必要かどうかを判断します。2。リストの要素配列に要素を配置します。

スレッドの不安定さの隠れた危険性(1):

リストの現在のサイズが8、つまりsize = 8であるとします。

スレッドAはaddメソッドの入力を開始します。つまり、サイズ8の値を取得し、ensureCapacityInternalメソッドを呼び出して容量を決定します。

スレッドBもこの時点でaddメソッドに入り、取得するサイズ値も8であり、ensureCapacityInternalメソッドの呼び出しも開始します。

スレッドAは、必要なサイズが8 + 1 = 9であり、elementDataのサイズがわずか9であることを検出しました。これは、対応できます。そのため、拡張して戻ることはありません。

スレッドBは、デマンドサイズが9であることも検出します。これは、対応して返すこともできます。

スレッドAは値の設定操作を開始し、elementData [size ++] = eを実行した後、サイズは9になります。

スレッドBも値操作の設定を開始し、elementData [size ++]、つまりelementData [ 9 ] = eを実行します。このとき、範囲外の配列例外ArrayIndexOutOfBoundsExceptionが報告されます。

スレッドの不安定さの隠れた危険性(2):

さらに、elementData [size ++] = e設定値の操作も、スレッドのセキュリティを低下させます。この操作はアトミック操作ではないため、次の2つのステップで構成されます。

1、elementData [size] = e;

2、サイズ=サイズ+1;

elementDataの現在のサイズが0であるとします。

スレッドAは、要素「A」追加しelementData添え字が0の位置に「A」を配置します

次に、スレッドBも要素「B」を追加し、最初の操作に進みました。このとき、スレッドBで取得したサイズ値は0のままなので、スレッドBもelementData添え字が0の位置に「B」配置します

次に、スレッドAはサイズの値を1に増やし、スレッドBはサイズの値を2に増やします。

スレッドAとBの実行が完了した後、elementDataの期待される結果は次のとおりです。サイズは2、elementData [0] = "A"、elementData [1] = "B"。

実際の状況は次のとおりです。サイズは2、elementData [0] = "B"、elementData [1] = nullです。


    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    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);
    }

 

おすすめ

転載: blog.csdn.net/sjmz30071360/article/details/89453947