0

私は Java API java.util.concurrent.atomic、特にAtomicIntegerクラスを調べてきました。

メソッドのコメントは、これらのメソッドがアトミックであることを示しています。

たとえば、 getAndIncrement ()を使用します。

public final int getAndIncrement() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))                
        return current;
    }
}

文書化されているように、これは「現在の値を原子的に1ずつインクリメントする」です。

このメソッドをアトミ​​ックにしているのは正確には何ですか? 私が見たところ、それは完全に「非アトミック」です-その実行に関与する多くのサイクルと、ステートメントの実行中

        int next = current + 1;

たとえば、nextの値は別のスレッドで設定できます。

4

4 に答える 4

2

原子性はcompareAndSet(current, next)メソッド内で処理されます。コードはインクリメントを行い、まだ変更されていない場合にのみ新しい値を設定します。これはアトミックに行われます (またはアトミックな動作を偽造します)。それ以降に変更されている場合は、もう一度試す必要があります。したがって、アトミックではないかもしれませんが、アトミックであるかのように動作します。

于 2013-10-21T10:30:58.233 に答える
1

AtomicInteger は、Integer Counter のスレッドセーフな実装のために、volatile と CAS (Compare and Swap) の組み合わせを使用します。

volatile 変数への読み取りと書き込みは、同期コード ブロックを使用してモニターを取得および解放する場合と同じメモリ セマンティクスを持ちます。したがって、揮発性フィールドの可視性は JMM によって保証されます。

AtomicInteger クラスはその値フィールドを volatile 変数に格納するため、従来の volatile 変数のデコレーターですが、ハードウェア レベルの CAS (比較と設定) のサポートを要求した後に値を更新するための独自のノンブロッキング メカニズムを提供します。スレッドの競合が低から中程度の場合、アトミック更新は、同期ブロッキングインクリメント操作と比較して、より高いスループットを提供します。

AtomicInteger クラスの getAndIncrement() メソッドの実装です。

public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }

値をインクリメントするためのロックは取得されず、無限ループ内で CAS が使用されて新しい値が更新されていることがわかります。

AtomicInteger はロックを必要としないため、スレッドの競合が低から中程度のスケーラブルなアプリケーションを作成するために使用できます。

于 2013-10-21T10:31:13.600 に答える
0

ループと CAS を使用しない場合に何が起こるかを見ると、ここでアトミックとは何を意味するのかが明確になります。

synchronized と compare と set がない場合 (ループは不要)、コードは次のようになります。

public final int getAndIncrement() {

        int current = get();
        int next = current + 1;
        set(next);
        return current;

}

マルチスレッド環境では、2 つのスレッドが現在の値を 0 と見なし、値を 1 に更新しようとする可能性があります。2 つの更新ですが、値は 1 つだけ増加します。明らかに、同期がなければ、これが起こらないという保証はありません。

したがって、1 にインクリメントする前に値がまだ 0 であるかどうかを確認するために、compareAndSet が役立ちます。したがって、compareAndSet の場合、現在の値がゼロの場合にのみ値を 1 に設定し、それ以外の場合は失敗します。上記の 2 つのスレッドが値を更新するケースを考慮すると、多くても 1 つのスレッドのみが値を 1 に正常に増加させ、他のスレッドは失敗します。したがって、操作の原子性 (「オール オア ナッシング」) を尊重します。

于 2013-10-21T10:45:37.757 に答える