380

いつ使用しAtomicReferenceますか?

すべてのマルチスレッド プログラムでオブジェクトを作成する必要がありますか?

AtomicReference を使用する必要がある簡単な例を提供します。

4

8 に答える 8

254

アトミック参照は、参照に対して単純なアトミック(つまり、スレッドセーフで自明ではない) 操作を行う必要があり、モニターベースの同期が適切でない設定で使用する必要があります。オブジェクトの状態が最後に確認したとおりのままである場合にのみ、特定のフィールドを確認したいとします。

AtomicReference<Object> cache = new AtomicReference<Object>();

Object cachedValue = new Object();
cache.set(cachedValue);

//... time passes ...
Object cachedValueToUpdate = cache.get();
//... do some work to transform cachedValueToUpdate into a new version
Object newValue = someFunctionOfOld(cachedValueToUpdate);
boolean success = cache.compareAndSet(cachedValue,cachedValueToUpdate);

アトミック参照セマンティクスのため、cacheオブジェクトがスレッド間で共有されている場合でも、 を使用せずにこれを行うことができますsynchronized。一般に、自分が何をしているのかわからない限り、シンクロナイザーまたはフレームワークをそのまま使用java.util.concurrentするよりも、シンクロナイザーまたはフレームワークを使用した方がよいでしょう。Atomic*

このトピックを紹介する 2 つの優れたデッド ツリー リファレンス:

(これが常に真であったかどうかはわかりません)参照割り当て(つまり=)自体がアトミックです(プリミティブな64ビット型の更新はアトミックではないlongか、アトミックではないかもしれませんが、参照doubleの更新は64ビットであっても常にアトミックです) を明示的に使用せずに. Java 言語仕様 3ed、セクション 17.7 を参照してください。Atomic*

于 2010-10-18T23:22:48.117 に答える
39

AtomicReference の使用例を次に示します。

数値範囲として機能し、個々の AtmomicInteger 変数を使用して数値の下限と上限を維持するこのクラスを検討してください。

public class NumberRange {
    // INVARIANT: lower <= upper
    private final AtomicInteger lower = new AtomicInteger(0);
    private final AtomicInteger upper = new AtomicInteger(0);

    public void setLower(int i) {
        // Warning -- unsafe check-then-act
        if (i > upper.get())
            throw new IllegalArgumentException(
                    "can't set lower to " + i + " > upper");
        lower.set(i);
    }

    public void setUpper(int i) {
        // Warning -- unsafe check-then-act
        if (i < lower.get())
            throw new IllegalArgumentException(
                    "can't set upper to " + i + " < lower");
        upper.set(i);
    }

    public boolean isInRange(int i) {
        return (i >= lower.get() && i <= upper.get());
    }
}

setLower と setUpper はどちらもチェックしてから実行するシーケンスですが、アトミックにするのに十分なロックを使用していません。数値範囲が (0, 10) を保持し、あるスレッドが setLower(5) を呼び出し、別のスレッドが setUpper(4) を呼び出した場合、不運なタイミングで両方がセッターのチェックに合格し、両方の変更が適用されます。その結果、範囲は現在 (5, 4) 無効な状態を保持しています。したがって、基礎となる AtomicIntegers はスレッドセーフですが、複合クラスはそうではありません。これは、上限と下限に個々の AtomicInteger を使用する代わりに AtomicReference を使用することで修正できます。

public class CasNumberRange {
    // Immutable
    private static class IntPair {
        final int lower;  // Invariant: lower <= upper
        final int upper;

        private IntPair(int lower, int upper) {
            this.lower = lower;
            this.upper = upper;
        }
    }

    private final AtomicReference<IntPair> values = 
            new AtomicReference<IntPair>(new IntPair(0, 0));

    public int getLower() {
        return values.get().lower;
    }

    public void setLower(int lower) {
        while (true) {
            IntPair oldv = values.get();
            if (lower > oldv.upper)
                throw new IllegalArgumentException(
                    "Can't set lower to " + lower + " > upper");
            IntPair newv = new IntPair(lower, oldv.upper);
            if (values.compareAndSet(oldv, newv))
                return;
        }
    }

    public int getUpper() {
        return values.get().upper;
    }

    public void setUpper(int upper) {
        while (true) {
            IntPair oldv = values.get();
            if (upper < oldv.lower)
                throw new IllegalArgumentException(
                    "Can't set upper to " + upper + " < lower");
            IntPair newv = new IntPair(oldv.lower, upper);
            if (values.compareAndSet(oldv, newv))
                return;
        }
    }
}
于 2015-05-29T10:00:20.647 に答える
9

AtomicReference はいつ使用しますか?

AtomicReferenceは、同期を使用せずに原子的に変数値を更新する柔軟な方法です。

AtomicReference単一変数でロックフリーのスレッドセーフプログラミングをサポートします。

高レベルの並行APIでスレッド セーフを実現する方法は複数あります。アトミック変数は、複数のオプションの 1 つです。

Lockオブジェクトは、多くの同時アプリケーションを簡素化するロック イディオムをサポートします。

Executorsスレッドを起動および管理するための高レベル API を定義します。java.util.concurrent によって提供される Executor 実装は、大規模なアプリケーションに適したスレッド プール管理を提供します。

同時収集により、大規模なデータ コレクションの管理が容易になり、同期の必要性を大幅に減らすことができます。

アトミック変数には、同期を最小限に抑え、メモリの一貫性エラーを回避するのに役立つ機能があります。

AtomicReference を使用する必要がある簡単な例を提供します。

サンプルコードAtomicReference:

String initialReference = "value 1";

AtomicReference<String> someRef =
    new AtomicReference<String>(initialReference);

String newReference = "value 2";
boolean exchanged = someRef.compareAndSet(initialReference, newReference);
System.out.println("exchanged: " + exchanged);

すべてのマルチスレッド プログラムでオブジェクトを作成する必要がありますか?

AtomicReferenceすべてのマルチスレッド プログラムで使用する必要はありません。

単一の変数を保護する場合は、を使用しますAtomicReference。コードブロックを保護したい場合は、Lock/synchronizedなどの他の構造を使用してください。

于 2016-05-20T06:05:53.803 に答える
-1

もう 1 つの簡単な例は、セッション オブジェクトでセーフ スレッドの変更を行うことです。

public PlayerScore getHighScore() {
    ServletContext ctx = getServletConfig().getServletContext();
    AtomicReference<PlayerScore> holder 
        = (AtomicReference<PlayerScore>) ctx.getAttribute("highScore");
    return holder.get();
}

public void updateHighScore(PlayerScore newScore) {
    ServletContext ctx = getServletConfig().getServletContext();
    AtomicReference<PlayerScore> holder 
        = (AtomicReference<PlayerScore>) ctx.getAttribute("highScore");
    while (true) {
        HighScore old = holder.get();
        if (old.score >= newScore.score)
            break;
        else if (holder.compareAndSet(old, newScore))
            break;
    } 
}

ソース: http://www.ibm.com/developerworks/library/j-jtp09238/index.html

于 2015-07-15T13:02:43.960 に答える