CAS(比較とスワップ)、すなわち、比較および交換、しばしば同時アルゴリズムで使用されている技術。
CASのアイデアは単純です:AとVの期待値が同じメモリ値である場合のみと、メモリ値をBと返すように変更された場合に三つのパラメータ、Vメモリの現在値、旧Aの期待値は、Bの値は、更新されます本当、何もしないとfalseを返しますか。
CAS原子のコンセプト。アトミック操作または一連の操作は中断されていません。そして、JavaでCASは、アトミックな動作を保証する一つの方法です。
最初からJava1.5、JDKの同時実行パッケージは、アトミック操作をサポートするためにいくつかのクラスを提供してアトミックに始めています。
私はこの操作を++原子性を保証することはできません揮発性と同様に、CASを保証することができます。
原子クラスの最初の、使用
AtomicIntegerには、例えば、一般的なAPIを使用しました。
- 公共の最終int型は、get():現在の値を取得します。
- 公共の最終int型getAndSet(int型newValueに):現在の値を取得し、新しい値を設定
- 公共の最終int型getAndIncrement():現在の値と増分を取得
- 公共の最終int型getAndDecrement():現在の値とデクリメントを取得します。
- 公共の最終int型getAndAdd(int型デルタ):現在の値を取得し、プラスの期待値
マルチスレッドの利点と比較して整数変数増加を可能にします。
プライベート 揮発 int型 COUNT = 0 ; // 実行スレッド数を++確保するためには、ロックが必要です 公共に 同期 のボイド(INCREMENTを){ COUNT ++ ; } 公共 のint 同様にgetCount(){ 戻りCOUNT; }
AtomicIntegerを使用した後:
プライベートのAtomicIntegerのCOUNT =の新しい新;のAtomicInteger()の 公共 空INCREMENT(){ count.incrementAndGet(); } // のAtomicIntegerを使用した後、ロックを必要としない、スレッドの安全性を達成することができる 公共 のint 同様にgetCount(){ リターンcount.get() ; }
二、CASの問題
悲観的ロックに同期楽観的ロックとしてCAS方式、。したがって、CASの使用は場合は並行性の問題、一般的に良いパフォーマンスを解決するために。
しかし、CASの方法を使用すると、いくつかの質問があります:
1.長いサイクルは、大きな時間を要します
時間が成功したスピンCASでない場合、それは非常に大きなCPUの実行コストをもたらすでしょう。
2.アトミック操作は、共有変数を保証することができます
共有変数を複数動作させる場合は、CASサイクルはアトミック操作を保証することはできません、この時間は、ロックを必要とします。
最初からのJava 1.5は、JDKクラスAtomicReferenceは、オブジェクト間の参照原子を確保するために提供しました。
3.ABA問題
値がある場合、それはBになった、彼はAで、その値が変更されますが、実際に変更されていないでしょうCAS検査を使用します。
バージョン番号を使用するソリューション。追加の可変バージョン番号、変数の更新バージョン番号は1だけインクリメントされるたびにの前に、その後、A-> B-> Aは1A〜> 2B-> 3Aなります。
Javaの1.5の当初から、JDKは、問題を解決するためにAtomicStampedReference、AtomicMarkableReferenceのABAのクラスを提供します。
三、CASは達成します
公共 最終 INT incrementAndGet(){ 戻り(unsafe.getAndAddIntをこの、valueOffset、1)+ 1 。 } パブリック 最終 INT getAndAddInt(オブジェクトO、長いオフセット、int型のデルタ){ int型V、 実行{ V = getIntVolatile(O、オフセット)。 } ながら(!compareAndSwapInt(O、オフセット、V、V + デルタ))。 リターンV; } パブリック ネイティブ INT getIntVolatile(オブジェクトO、長いオフセット)。 公 最終の ネイティブ ブール(オブジェクトo compareAndSwapInt 長いオフセット、int型、予想INT X)。
最後の呼び出しは、 安全でないクラスのメソッド、主にcompareAndSwapInt方法、すなわちCAS。
安全でない実現のを見る:https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/fea2c7f50ce8/src/share/vm/prims/unsafe.cpp
UNSAFE_ENTRY(jboolean、Unsafe_CompareAndSwapInt(JNIEnvの* ENVは、jオブジェクト危険JINT x)から、オフセット、JINT eをjlong、OBJ jオブジェクト) UnsafeWrapper(" Unsafe_CompareAndSwapInt " )。 OOPのp = JNIHandles ::決意(OBJ)。 JINT * ADDR =(JINT * )index_oop_from_field_offset_long(P、オフセット)。 リターン(JINT)(アトミック:: CMPXCHG(X、ADDR、E))== E; UNSAFE_END
xは、パラメータ値が更新されている前記コアメソッド原子:: CMPXCHG(X、ADDR、e)は、パラメータeは、メモリアドレスaddrパラメータに、元のメモリの値です。
アトミック実現のを見る:https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/fea2c7f50ce8/src/share/vm/runtime/atomic.cpp
#include " ランタイム/ atomic.inline.hpp " jbyte原子:: CMPXCHG(jbyteのexchange_value、揮発性 jbyte * DEST、jbyteのcompare_value){ (アサートはsizeof(jbyte)== 1、" 仮定します。" )。 uintptr_tをdest_addrは = (uintptr_tを)DEST。 オフセットいるuintptr_t = dest_addrは%以下のはsizeof (JINT)を、 揮発性 JINT * dest_int =(揮発性 JINT *)(dest_addrは- オフセット)。 JINT CUR = * dest_int。 jbyte * cur_as_bytes =(jbyte *)(&CUR)。 JINT new_val = CUR。 jbyte * new_val_as_bytes =(jbyte *)(&new_val)。 new_val_as_bytes [オフセット] = exchange_value。 ながら(cur_as_bytesは、[オフセット] == compare_value){ JINT RES = CMPXCHG(new_val、dest_int、CUR)。 もし(解像度== CUR)ブレーク。 CUR =の解像度。 new_val = CUR。 new_val_as_bytes [オフセット] = exchange_value。 } 戻りcur_as_bytes [オフセット]。 }
ビューatomic_linux_x86.inline.hpp、異なるシステム、異なるCPUが異なる実装を持っている:https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/fea2c7f50ce8/src/share/vm/runtime/atomic.inline .HPP
Linux x86の実現:https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/fea2c7f50ce8/src/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp
インラインJINT原子:: CMPXCHG(JINT exchange_value、揮発性 JINT * DEST、JINTのcompare_value){ int型 MP = OSを:: is_MP()。 __asm__ 揮発性(LOCK_IF_MP(%4)" cmpxchgl%1(%3)" :" = A " (exchange_value) :" R "(exchange_value)、" "(compare_value)、" R "(DEST)、「R 「(MP) :CC " " メモリ" ); 戻りexchange_value; }
Windowsのx86の-実現:https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/fea2c7f50ce8/src/os_cpu/windows_x86/vm/atomic_windows_x86.inline.hpp
インラインJINTアトミック:: CMPXCHG(JINT exchange_value、揮発性 JINT * DEST、JINTのcompare_value){ // InterlockedCompareExchangeための代替 int型融点= OS :: is_MP(); __asm { MOV EDX、DEST MOV ECX、exchange_value MOV EAX、compare_value LOCK_IF_MP(MP) CMPXCHG用のDWORD PTR [EDX]、ECX } } // MPマシン上の命令にロックプレフィックスを追加 // VC ++はロックプレフィックスを好きではありません単一の行にあることを // ので、ロックの接頭辞の後にラベルを挿入することはできません。 //ロックプレフィックスを放出することによって、我々はそれの後にラベルを定義することができます。 #define LOCK_IF_MP(MP)__asmのCMPのMP、0 \ __asmはje L0 \ __asm _emit 0xF0が\ __asm L0: