質問

値を別のものと原子的に交換できるタイプの参照を実装する方法はありますか?


Javaにはあります AtomicReference ローカル変数と交換できますが、別の変数と交換できます AtomicReference.

できるよ:

AtomicReference r1 = new AtomicReference("hello");
AtomicReference r2 = new AtomicReference("world");

2つの操作の組み合わせでそれらを交換します。

r1.set(r2.getAndSet(r1.get()));

しかし、これはそれらを中間に一貫性のない状態にします。 "hello". 。また、原子的に交換できたとしても、(ペアとして)原子的に読むことができませんでした。


私ができることを望んでいるのは、次のとおりです。

PairableAtomicReference r1 = new PairableAtomicReference("hello");
PairableAtomicReference r2 = new PairableAtomicReference("world");
AtomicRefPair rp = new AtomicRefPair(r1, r2);

それから

Object[] oldVal, newVal;
do {
    oldVal = rp.get();
    newVal = new Object[] {oldVal[1], oldVal[0]};
} while (! rp.compareAndSet(oldVal, newVal));

値を交換し、別のスレッドで:

AtomicRefPair otherRP = new AtomicRefPair(r1, r2);
System.out.println(Arrays.toString(otherRP.get()));

また、出力がどちらかになることを確認してください [hello, world] また [world, hello].

ノート:

  • r1r2 この操作のためにペアになっていますが、別のスレッドが独立してペアになる可能性があります。 r1 そして別の r3 (残念ながら、それは私が使用できないことを意味します このソリューション.)
  • これらの参考文献は数十万人であるため、グローバル ReentrantLock 主要なボトルネックになります。
  • rpotherRP 必ずしもスレッド間で共有されるわけではないため、単純にロックすることは機能しません。彼らはそうかもしれません インターン, 、しかし、インターンプールには、別のボトルネックとなる独自の同期が必要です。
  • ここでは2つの参照のグループしか作成していませんが、グループ3以上の能力はボーナスになります。

ロックフリーバージョンを実装することは可能ですか? AtomicRefPair?そうではないことを私は持っていますが、そうでない場合は、なぜ説明する場所があるのでしょうか?


関連している: C#で2つのintを原子的に交換するにはどうすればよいですか?

役に立ちましたか?

解決

素晴らしい解決策があるかどうかはわかりませんが、次の醜い人はうまくいきます。

public final class MyReference<T> extends ReentrantLock implements Comparable<MyReference<T>> {
    public MyReference() {
        id = counter.incrementAndGet();
    }

    public void swap(MyReference<T> other) {
        if (id < other.id) {
            lock();
            other.lock();
        } else {
            other.lock();
            lock();
        }
        final T tmp = value;
        value = other.value;
        other.value = tmp;
        unlock();
        other.unlock();
    }

    public static <T> List<T> consistentGet(List<MyReference<T>> references) {
        final ArrayList<MyReference<T>> sortedReferences = Lists.newArrayList(references);
        Collections.sort(sortedReferences);
        for (val r : sortedReferences) r.lock();
        final List<T> result = Lists.newArrayListWithExpectedSize(sortedReferences.size());
        for (val r : references) result.add(r.value);
        for (val r : sortedReferences) r.unlock();
        return result;
    }

    @Override
    public int compareTo(MyReference<T> o) {
        return id < o.id ? -1 : id > o.id ? 1 : 0;
    }

    private final static AtomicInteger counter = new AtomicInteger();

    private T value;
    private final int id;
}
  • AtomicReferenceの代わりにMyReferenceを使用します。
  • 多くのロックを使用していますが、それらのどれもグローバルではありません。
  • 固定順序でロックを取得するため、デッドロックがありません。
  • LombokとGuavaを使用してコンパイルします(それらを使用してPseudocodeとして使用します)。

他のヒント

ペアを保持している不変のクラスを持っています。それがあなたの原子です。ペアを交換することは、原子を交換することを意味します。

更新:あなたの質問はあまり明確ではありません。しかし、一般的に、複数の変数で構成される同時システムの場合、望むかもしれません

  1. システム状態のスナップショットを取ります。スナップショットは、取得されたら変更されません。
  2. 複数の変数を一度に変更することにより、システム状態を原子的に更新します。私の更新の間に以前のスナップショット(私の計算が基づいていた)の間に他の更新がないことが必要になる場合があります

システムがあまりにも多くのリソースを消費しない場合、システムをスナップショットで直接モデル化できます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top