AtomicReferenceFieldUpdater - メソッドset、get、compareAndSetセマンティクス

From the Java AtomicReferenceFieldUpdater docs:

このクラスの compareAndSet メソッドの保証は   他の原子クラスよりも弱い。このクラスでは保証できないため   フィールドのすべての用途が原子の目的に適していること   アクセス、それはアトミック性と揮発性セマンティクスを    compareAndSet set の他の呼び出しに対して、

つまり、 compareAndSet と一緒に通常のvolatile書き込みはできませんが、代わりに set を使用する必要があります。 get については何も言及していません。

これは、同じ原子性保証のvolatileフィールドを読み込めることを意味しますか? set または compareAndSet の前のすべての書き込みは、

または、フィールドの揮発性読み込みの代わりに AtomicReferenceFieldUpdater get を使用する必要がありますか?

参照があれば投稿してください。

ありがとうございました。

編集:

Java Concurrency in Practice から、彼らが言う唯一のこと:

アップデータクラスのアトミック性の保証は、   通常の原子クラスを使用することは保証できません   基礎となるフィールドは直接変更されません - compareAndSet   算術方法は他のものに対してのみ原子性を保証する   スレッドは、アトミックフィールドアップデータメソッドを使用しています。

ここでも、他のスレッドがこれらの揮発性フィールドをどのように読み込むのかについての言及はありません。

また、「直接修正」が定期的な揮発性書き込みであると想定するのは正しいですか?

28
リフレクションを使用してフィールドの読み取り/書き込みを行わない限り、揮発性の読み取り/書き込みが可能です。
追加された 著者 bestsss,
ConcurrentSkipListMapのソースコードの見た目のソースを見たい場合は、AtomicReferenceFieldUpdarerはヘッドのCASに対してのみ使用され、読み込みは通常の揮発性の読み込みです。 CASが提供する唯一の便利な操作であり、残りはフィラーです。
追加された 著者 bestsss,
参照への書き込みは、どちらの方法でも不可能であるか、または揮発性ではありません。唯一のアトミック操作は、読み取り - >比較 - >変更のため、CASです。読み取りと書き込みは常に原子単位で行われます。つまり、半分の有効な参照を取得できます。揮発性の読み取りは、以前の読み取りと比較して読み取り/読み取りバリアを保証します。したがって、形状や形が特にない場合は、特にgetを使用する必要があります。
追加された 著者 bestsss,
あなたは「あなたは揮発性の読み書きができると思う」という意味を詳しく説明できますか? 「仮定する」とはどういう意味ですか? APIは明示的にあなたに揮発性の書き込みを行うことはできないと明言していますが、それは原子であると予想します。代わりに set メソッドを使用する必要があります。問題は、アトミック性を確保するために揮発性の読み取りではなく get メソッドを使用する必要があるかどうかです。
追加された 著者 axel22,

3 答え

atomicsのパッケージのドキュメントで説明されているように(一般的に、アップデータは特にありません)。

The memory effects for accesses and updates of atomics generally follow the rules for volatiles, [...]:

  • get has the memory effects of reading a volatile variable.
  • set has the memory effects of writing (assigning) a volatile variable.
  • [...]
  • compareAndSet and all other read-and-update operations such as getAndIncrement have the memory effects of both reading and writing volatile variables.

原子の compareAndSet が解決しようとしている問題は何ですか? の代わりに atomicInteger.compareAndSet(1,2)を使用する理由(volatileInt == 1){volatileInt = 2; } ?同時読み込みの問題を解決しようとしているのは、 volatile です。 (「揮発性」の読み込みまたは書き込みは、「アトミック」読み込みまたは書き込みと同じです。)同時読み込みは、書き込みの途中で発生した場合、または文が問題のある方法で並べ替えまたは最適化された場合にのみ問題になります。 volatileInt のアプローチでは、 volatile のアプローチでは、他のスレッドが入ってくる可能性があるという compareAndSet volatileInt volatileInt == 1 )とそれに書き込むとき( volatileInt = 2 /code>)。 compareAndSet は、その間に競合する書き込みをロックアウトすることでこの問題を解決します。

This is equally true in the specific case of the "updaters" (AtomicReferenceFieldUpdater etc.): volatile reads are still just peachy. The updaters' compareAndSet methods' only limitation is that, instead of "locking out any competing writes" as I wrote above, they only lock out competing writes from the same instance of AtomicReferenceFieldUpdater; they can't protect you when you're concurrently updating a volatile field directly (or, for that matter, when you're concurrently using multiple AtomicReferenceFieldUpdaters to update the same volatile field). (Incidentally, depending how you look at it — the same is true of AtomicReference and its kin: if you were to update their fields in a way that bypassed their own setters, they couldn't protect you. The difference is that an AtomicReference actually owns its field, and it's private, so there's no need to warn you against somehow modifying it by external means.)

あなたの質問に答えるには:はい、部分的な/一貫性のない読み取り、並べ替えられたステートメントに対する同じアトミック性の保証を持つ volatile フィールドを引き続き読み取ることができます。


Edited to add (Dec 6): Anyone who's particularly interested in this subject will probably be interested in the discussion immediately below. I was asked to update the answer to clarify salient points from that discussion:

  • I think the most important point to add is that the above is my own interpretation of the documentation. I'm fairly confident that I have understood it correctly, and that no other interpretation makes sense; and I can, if desired, argue the point at length ;-) ; but neither I nor anyone else has produced any references to any authoritative document that addresses this point any more explicitly than the two documents mentioned in the question itself (the class's Javadoc and Java Concurrency in Practice) and the one document mentioned in my original answer to it above (the package's Javadoc).

  • The next most important point, I think, is that although the documentation for AtomicReferenceUpdater says that it's unsafe to mix compareAndSet with a volatile write, I believe that on typical platforms it actually is safe. It's unsafe only in the general case. I say this because of the following comment from the package documentation:

    The specifications of these methods enable implementations to employ efficient machine-level atomic instructions that are available on contemporary processors. However on some platforms, support may entail some form of internal locking. Thus the methods are not strictly guaranteed to be non-blocking -- a thread may block transiently before performing the operation.

    So:

    • In a typical JDK implementation for a modern processor, AtomicReference.set simply uses a volatile write, since AtomicReference.compareAndSet uses a compare-and-swap operation that is atomic with respect to volatile writes. AtomicReferenceUpdater.set is necessarily more complex than AtomicReference.set, because it has to use reflection-like logic to update a field in another object, but I maintain that that is the only reason it is more complex. A typical implementation calls Unsafe.putObjectVolatile, which is a volatile write by longer name.
    • But not all platforms support this approach, and if they don't, then blocking is permitted. At the risk of oversimplifying, I take this to mean roughly that an atomic class's compareAndSet could be implemented by (more or less) applying synchronized to a method that uses get and set straightforwardly. But for this to work, set must also be synchronized, for the reason explained in my original answer above; that is, it can't just be a volatile write, because then it could modify the field after compareAndSet has called get but before compareAndSet calls set.
    • Needless to say, my original answer's use of the phrase "locking out" shouldn't be taken literally, since on a typical platform nothing very lock-like need occur.
  • In Sun's JDK 1.6.0_05 implementation of java.util.concurrent.ConcurrentLinkedQueue, we find this:

    private static class Node {
        private volatile E item;
        private volatile Node next;
        private static final AtomicReferenceFieldUpdater nextUpdater =
            AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "next");
        private static final AtomicReferenceFieldUpdater itemUpdater =
            AtomicReferenceFieldUpdater.newUpdater(Node.class, Object.class, "item");
        Node(E x) { item = x; }
        Node(E x, Node n) { item = x; next = n; }
        E getItem() { return item; }
        boolean casItem(E cmp, E val)
            { return itemUpdater.compareAndSet(this, cmp, val); }
        void setItem(E val) { itemUpdater.set(this, val); }
        Node getNext() { return next; }
        boolean casNext(Node cmp, Node val)
            { return nextUpdater.compareAndSet(this, cmp, val); }
        void setNext(Node val) { nextUpdater.set(this, val); }
    }
    

    (note: whitespace adjusted for compactness), where, once an instance has been constructed, there are no volatile writes — that is, all writes are via AtomicReferenceFieldUpdater.compareAndSet or AtomicReferenceFieldUpdater.set — but volatile reads appear to be used freely, without a single call to AtomicReferenceFieldUpdater.get. Later releases of JDK 1.6 were changed to use Unsafe directly (this had happened by Oracle's JDK 1.6.0_27), but discussions on the JSR 166 mailing list attribute this change to performance considerations rather than to any qualm about the correctness of the previous implementation.

    • But I must point out that this is not bullet-proof authority. For convenience, I write of "Sun's implementation" as though it had been a unitary thing, but my previous bullet-point makes obvious that JDK implementations for different platforms may have to do things differently. The above code seems to me to have been written in a platform-neutral way, since it eschews plain volatile writes in favor of calls to AtomicReferenceFieldUpdater.set; but someone who doesn't accept my interpretation of the one point may not accept my interpretation of the other, and might argue that the above code is not meant to be safe for all platforms.
    • Another weakness of this authority is that, although Node seems to allow volatile reads to take place concurrently with calls to AtomicReferenceFieldUpdater.compareAndSet, it's a private class; and I have not undertaken any proof that its owner (ConcurrentLinkedQueue) actually makes such calls without its own precautions. (But although I have not proven the claim, I doubt that anyone would dispute it.)

この付録の背景については、以下のコメントを参照してください。

23
追加された
偉大な答え、それはほとんどすべてをカバーしています。
追加された 著者 nvrs,
@BegemoT:私が理解しているように、 "普通のAtomicXXXクラスはset()をちょうどvalue = newValueとして実装しています"ということはまったく真実ではありません。 「これらのメソッドの仕様では、現行のプロセッサで利用可能な効率的なマシンレベルのアトミック命令を実装することができますが、一部のプラットフォームでサポートする場合、何らかの内部ロックが発生する可能性があります。ノンブロッキングである - スレッドは、操作を実行する前に一時的にブロックすることがあります。だから、多くのプラットフォームでは、 set() value = newValue
追加された 著者 ruakh,
@BegemoTとaxel22:O.K.、私はそれを打ちました。補遺は元の答えの2倍以上ですので、良いショットではありませんが、何かです。 :-P。 。 。とにかく、言うまでもなく、私はそれ以上のコメントを歓迎します。
追加された 著者 ruakh,
@BegemoT:これは私の解釈です。上で述べたように、私はそれが唯一の解釈だと思っています。私は他の一貫した方法で文書を読むことはできないと思っています。例えば、JSR166メーリングリストの特定の投稿) - しかし、私はその "公式"の約束を約束することはできません。 JDKについて:JDKが__Updater.CASを揮発性の読み込み(AFAICTは安全)と混在させるケースを見てきましたが、揮発性の書き込みのある.CAS(一般的なケースでは安全でないと記載されています)。
追加された 著者 ruakh,
@BegemoT:違いは compareAndSet (およびその他の読み取り+更新操作)の存在です。揮発性書込みは、揮発性読出しおよび他の揮発性書込みに関して様々な保証を提供する。アトミック書き込みは、アトミック読み取り、その他のアトミック書き込み、およびアトミック読み取り+更新操作に関して同じ保証を提供します。 (あなたが volatile synchronized だけを使ってアトミックを実装しなければならないとしたら、 compareAndSet set compareAndSet のvolatile readとvolatile writeの間でvolatile書き込みを実行する可能性があります。
追加された 著者 ruakh,
Javaメモリモデルと矛盾しない方法で compareAndSet を実装するには、ある程度のロックが必要となるため、このような実装は不可能です。
追加された 著者 ruakh,
volatile で十分であるため、 .compareAndSwapObject() .getOrderedObject()
追加された 著者 ruakh,
@ axel22:Jc JSR-166エキスパートグループのメンバーの助けを借りてDoug LeaとMartin Buchholzによって書かれた、 ConcurrentLinkedQueue のソースコードです。パブリックドメイン "は、Java 5の場合: AtomicReferenceFieldUpdater.set(...) .compareAndSet(...)を使用しますが、 。 (Java 6および7では、パフォーマンス上の理由から、 AtomicReferenceFieldUpdater ではなく Unsafe を直接使用するように変更されています。 ;しかし、そこには .putOrderedObject()
追加された 著者 ruakh,
@ axel22:私が引用した節は、アップデーターを含むすべてのアトミックに適用されます。私の答えの残りの部分は、あなたがすでに読んだドキュメンテーションに何があるのか​​を説明し、なぜそれがその方法であるのかを説明することでした。 compareAndSet + volatile-readが特に安全であることを明示的に示している権威のあるリファレンスを希望したいと思いますか?悲しいことに、私は1つも持っていない、申し訳ありません。私の答えはあなたが見た文書を解釈する唯一の方法だと思います。しかし私はそれが解釈であることを認めなければならない。あなたが確信していないなら、私はより多くの議論を提供することができますが、それ以上の言及はありません。ごめんなさい。
追加された 著者 ruakh,
ありがとうございました。更新者に関する段落の参照を提供できますか?
追加された 著者 axel22,
すべての答えのうち、これは最も理にかなっています。いくつかの同時データ構造のソースコードへの参照は、これらの主張を確認する実装が素晴らしいものになるでしょう。
追加された 著者 axel22,
合意 - コメント内の情報が回答自体にマージされるのが理想的です。
追加された 著者 axel22,
通常のAtomicXXXクラスはset()をちょうどvalue = newValueとして実装しています。 AtomicXXX.value自体が揮発性であるため、そのフィールドへのリフレクションベースの書き込み(まだ、私はできる)反射は揮発性修飾語の影響を取り消しません。ですから、At​​omicXXX.CAS/setペアの相互運用とAtomicUpdater.CAS/set /直接フィールドアクセスの相互運用の間に違いはないはずです。
追加された 著者 BegemoT,
@ruakh:はい、それは法的解釈のようです。しかし、AtomicXXX.set()はちょうどvolatileストアと同じセマンティクスを持っています。ですから、A.set()を実装するために必要なロックがあれば、単純なvolatileストアでも同じことが起こります。そして、再び、違いは何かが明らかではありません
追加された 著者 BegemoT,
@ruakhさて、私はそれを持っている。アトミックRMW操作のハードウェアサポートが不足している場合は、単純な「揮発性」と「アトミック」を区別することを余儀なくされています。同じ(ロックベースの)実装を共有することは、単純な揮発性を非常に非効率的にする可能性があり、適切ではありません。どのように "公式"がこの解釈であるかについて残された質問だけ?それは1)今日はハードウェアがCAS-esを持っていないのは非常に珍しいことです2)Updater.CASの多くの例が単純な揮発性物質と相互運用しているのを見ました。 jdkでも、私は思う...
追加された 著者 BegemoT,
@ruakhはい、あなたの説明に感謝します - 今はかなり合法的なようですが、確かに、javadocの背後にある理由を一貫して説明する唯一の解釈のようです。私はあなたの元の答えを更新して、私たちがここでコメントしたいくつかの点を、コメントで明確にするように頼むことができますか?それは一見するには十分明確ではないようです。私は、CASミスしたハードウェア上のアトミックな実装によるaddindの例は、推論を大きくはっきりさせることができると思います。私は編集後にあなたの答えをupvoteすることができます:)
追加された 著者 BegemoT,
@ruakh私は並行性の問題グループに質問を投稿しました。あなたの意見に正式なサポートが得られたようです。ユーザーは正しいです。ロックを必要とする実装では、フィールドアップデータクラスのどれも、フィールドアップデータの他のメソッドを除いて、アトミック性を保証できません。実際には、私はAtomicLongFieldUpdaterがロックベースの実装を実際に持っていることを認識しています。 David Holmes
追加された 著者 BegemoT,

これは質問の正確な答えではありません:

説明や意図は、ドキュメントからはっきりしていません。 IBM PowerやARMのようなグローバルな注文を揮発性の書き込みでバイパスし、フェンシングなしでCAS(LoadLinked/StoreCondition)の動作を公開するというアイデアなら、かなりの努力と混乱の原因になります。

sun.misc.UnsafeのCASには、仕様や注文保証はありません(これまでに起こったこととして知られています)が、java.util.atomic ...はそうです。だから、より弱いモデルjava.util.atomic impl。この場合、Java仕様に従うために必要なフェンスが必要になります。

アップデータクラスに実際にフェンスがないと仮定します。 そうであれば、フィールドの揮発性読み取り(getを使用しない)は更新値を返すはずです。つまり、明らかに get()は不要です。発注保証がないため、以前の店舗は伝播していない可能性があります(弱いモデルの場合)。 x86/SparcのTSOハードウェアはJava仕様を保証します。

しかし、これはまた、次の非揮発性リードでCASを並べ替えることができることを意味します。 java.util.concurrent.SynchronousQueue キューから興味深いノートがあります:

       //Note: item and mode fields don't need to be volatile
       //since they are always written before, and read after,
       //other volatile/atomic operations.

言及されたすべての原子操作は、正確にAtomicReferenceFieldUpdaterのCASです。これは、通常の読み込みと書き込みとAtomicReferenceFieldUpdater.CASとの間の不足または言い回し、すなわち揮発性書き込みのような動作を意味する。

        s.item = null;  //forget item
        s.waiter = null;//forget thread

        //....

        while ((p = head) != null && p != past && p.isCancelled())
            casHead(p, p.next);

ちょうどCAS、揮発性の書き込みはありません。

上記の条件を前提として、私はAtomicXXXFieldUpdaterがAtomicXXXのものと同じセマンティクスを公開していると結論づけます。

2
追加された

つまり、オブジェクトへの参照は保証されますが、オブジェクトを使用できるため、別のスレッドがオブジェクトにアクセスするときにそのオブジェクトのフィールドが正しく書き込まれないことがあります。

保証される唯一の方法は、フィールドが最終的か揮発性かを判断することです。

2
追加された
また、これらのフィールドは、アトミック・フィールド・アップデータで使用するために、最初は揮発性でなければなりません。
追加された 著者 axel22,
言い換えれば、アトミックリファレンスアップデータの set は、変更される特定のフィールドのみを保証しますか?それは通常の揮発性の書き込みのようではないのですか?このドキュメントでは、 "...同じフィールド上のcompareAndSetとset setの他の呼び出しに対してのみ、原始性と揮発性の意味を保証できます"とは言いません。これには weakCompareAndSet があります。
追加された 著者 axel22,