スレッドが失敗したり、他のスレッドを中断していない場合も、障害が発生するか、そしてこのアルゴリズムが呼び出されたハングノンブロッキング・アルゴリズムを。CASは、非ブロッキングアルゴリズムの一種でも楽観的ロックの技術です。それは、ロックを使用せず、またそうCASロックフリーアルゴリズムを複数のスレッド間で変数の同期を達成することができます。
1、CASの実装
メモリ位置Vを読み書きする必要がある、新しい値A及びBの比較提案書き込み - CASは、3つのオペランドを含みます 値がV Aに等しい場合にのみ、かつ、CASがV原子の値によって新しい値Bで更新されます、または新しい値を書き込まない場合は、のは、CASのアナログ実装を見てみましょう。
//CAS模拟实现
public class SimulatedCAS {
private int value;
public synchronized int get() {
return value;
}
public synchronized int compareAndSwap(int expectedValue, int newValue) {
int oldValue = value;
if (oldValue == expectedValue) {
value = newValue;
}
return oldValue;
}
public synchronized boolean compareAndSet(int expectedValue, int newValue) {
return (expectedValue == compareAndSwap(expectedValue, newValue));
}
}
复制代码
SimulatedCAS
シミュレーションのみCASがあるので、CASはその意味を理解し、アナログ実装なので、コードは非常に簡単です。
//基于CAS实现的一个线程安全计数器
public class CasCounter {
private SimulatedCAS value;
public int getValue() {
return value.get();
}
public int increment() {
int v;
do {
v = value.get();
} while (v != value.compareAndSwap(v, v + 1));
return v + 1;
}
}
复制代码
CasCounter
基づいてSimulatedCAS
実装され、スレッドセーフなカウンター、CasCounter
それはブロックされませんが、他のスレッドが同時に更新カウンタを更新する場合、それは複数回再試行します。理論的には、他のスレッドが再試行するたびに、スレッド、あなたがCASを競うたびに勝つために常にある場合は、しかし、飢餓の問題のこのタイプの実際には非常に少ないです。
2、アトミック変数クラス
Java5.0基礎となるint型で導入サポート、長い、およびオブジェクト参照も、CAS操作の他のタイプ、およびJVMは、基盤となるハードウェアを提供し、それらをコンパイルするための最も効果的な方法を開示しています。プラットフォームでは、ランタイムは、適切な(複数の)マシン命令としてそれらをコンパイルし、CASをサポートしています。最悪の場合、CASは命令をサポートしていない場合、JVMは、スピンロックを使用します。そのため、密接に関連プラットフォーム/コンパイラでJavaが提供するCASソリューションは、その性能にも影響を与えるプラットフォーム/コンパイラの影響を受けています。
アトミック変数クラス(例えばjava.util.concurrent.atomic
におけるAtomicXxx
この高効率CAS操作の使用に関する)。
タイプ | 実装クラス | 説明 |
---|---|---|
基本データ型 | AtomicInteger | 整数型アトミック更新 |
AtomicBoolean | ブール型のアトミック更新 | |
AtomicLong | ロングタイプの原子更新 | |
配列型 | AtomicIntegerArray | アトミック更新配列型の整数 |
AtomicLongArray | 長い配列型アトミック更新 | |
AtomicReferenceArray | アトミック更新対象の配列型 | |
参照型 | AtomicReference | アトミック更新オブジェクト |
AtomicStampedReference | アトミック更新オブジェクトは、ABAの問題を解決します | |
AtomicMarkableReference | アトミック更新オブジェクトは、ABAの問題を解決します | |
更新フィールドタイプ | AtomicIntegerFieldUpdater | 既存のタイプの揮発性アトミック更新整数、リフレクションを使用して実装しました |
AtomicLongFieldUpdater | 既存のロングタイプの揮発性アトミック更新は、リフレクションを使用して実装しました | |
AtomicReferenceFieldUpdater | 達成するためにリフレクションを使用してオブジェクトを既存の揮発性アトミック更新 |
上記のすべてJavaでアトミック変数クラスで、基本的には良く理解されているが、個々のはまだいくつかの明確化が必要です。
AtomicStampedReference
そしてAtomicMarkableReference
彼らは、解決するために設計されてABAの問題が存在します。しかし、彼らが異なっている、AtomicStampedReference
避けるようにオブジェクトプラスバージョン番号への参照であるABAの問題を、そしてAtomicMarkableReference
回避するために、オブジェクトプラスマーク(boolean型)への参照であるABAの問題を。
ドメインアップデータ原子と呼ばれるアップデートのフィールドタイプ、従来のvolatile
「ビュー」の反射に基づく1は、従来では可能であるvolatile
CASフィールドの使用。これは、クラスの平均原子番号よりも弱い保証の原子性を提供し、それは直接基本フィールドを直接変更していないことを確認することはできません- compareAndSet
他の方法は、他の方法アップデータスレッドを使用して、アトミック演算を確保することができます。
以下の点を考慮AtomicInteger
のソースコードを。
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
//valueOffset可以说是value在JVM中的虚拟内存地址
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
//传入初始化值
public AtomicInteger(int initialValue) {
value = initialValue;
}
//无参
public AtomicInteger() {
}
//返回当前值
public final int get() {
return value;
}
//设置新值
public final void set(int newValue) {
value = newValue;
}
//1.6新增方法,设置新值,但不会立即刷新,性能比set方法好
public final void lazySet(int newValue) {
unsafe.putOrderedInt(this, valueOffset, newValue);
}
//设定新值并返回旧值
public final int getAndSet(int newValue) {
return unsafe.getAndSetInt(this, valueOffset, newValue);
}
//比较并替换,Java CAS实现方法
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
//比较并替换,Java CAS实现方案,与compareAndSet实现一样名单不排除未来会有不同实现
public final boolean weakCompareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
//以原子方式将当前值增加1。返回旧值
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
//以原子方式将当前值减1。返回旧值
public final int getAndDecrement() {
return unsafe.getAndAddInt(this, valueOffset, -1);
}
//以原子方式将给定值添加到当前值。返回旧值
public final int getAndAdd(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta);
}
//以原子方式将当前值增加1。返回新值
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
//以原子方式将当前值减1。返回新值
public final int decrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
}
//以原子方式将给定值添加到当前值。返回新值
public final int addAndGet(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}
//1.8新增方法,更新并返回旧值
public final int getAndUpdate(IntUnaryOperator updateFunction) {
int prev, next;
do {
prev = get();
next = updateFunction.applyAsInt(prev);
} while (!compareAndSet(prev, next));
return prev;
}
//1.8新增方法,更新并返回新值
public final int updateAndGet(IntUnaryOperator updateFunction) {
int prev, next;
do {
prev = get();
next = updateFunction.applyAsInt(prev);
} while (!compareAndSet(prev, next));
return next;
}
//1.8新增方法,更新并返回旧值
public final int getAndAccumulate(int x,
IntBinaryOperator accumulatorFunction) {
int prev, next;
do {
prev = get();
next = accumulatorFunction.applyAsInt(prev, x);
} while (!compareAndSet(prev, next));
return prev;
}
//1.8新增方法,更新并返回新值
public final int accumulateAndGet(int x,
IntBinaryOperator accumulatorFunction) {
int prev, next;
do {
prev = get();
next = accumulatorFunction.applyAsInt(prev, x);
} while (!compareAndSet(prev, next));
return next;
}
//都是些类型转换的实现
...
}
复制代码
valueOffset
CASは、メモリ位置Vに言われているJVMのメモリアドレス、変数であります set
そしてlazySet
、compareAndSet
とweakCompareAndSet
方法の二組を見てする必要があります。
のでvalue
れるvolatile
修正お電話の際は、そのset
方法に、すべてのスレッドのvalue
値はすぐに更新され、そしてlazySet
、唯一の時間は、新しい値が更新され、再書き込み、まだ古い値を読み取ることはありません。これらの2つの方法の違いで、参照が可能JUCはAtomicXXXは区別ツール、セット、およびlazySet原子、SET対のAtomicInteger lazySet FCの 2の記事を。
compareAndSet
そして、weakCompareAndSet
まさに同じことを達成するため、これらの2つの方法は、より興味深いものです、それはなぜナイジェリアのですか?でスタックオーバーフローこれらの2つの方法の違いを参照することができますについての言うように、Javaは、将来的に異なる実装を有することができるweakCompareAndSet場合は、完全のcompareAndSetとして、どのように失敗した場合- Javaの?この記事。
でAtomicInteger
、その他のアトミック変数クラスが使用しているUnsafe
、それは安全でないため、CAS、それらのほとんどは、ネイティブメソッドおよび一般に公開されていないクラスを実装します。このクラスのハードウェア関連、CPUの命令レベルの操作されている、唯一の一歩アトミック操作するだけでなく、ロックを決定するために、オペレーティングシステムを要求してCASの問題を回避するには、取得するためにCPU内で直接オペレーティングシステムを気にしないので、効率が非常に高く、スレッドの安全性。
三の大問題に関する3、CASアトミック操作
CASは、次のような問題です。
- ABAの問題。
- CASは、CPUの命令レベルの操作ですので、高いCPU負荷を引き起こす可能性があるとき、サイクル時間が長すぎます。
- 変数の同時動作トラブルのビットを共有する複数の変数を操作することができます。あなたはできる
AtomicReference
解決されます。
ABAの問題は、VはAに等しい場合にのみ、CASがアトミックで新しい値B値Vで更新する場合は、意味論を検討するCAS、CASで珍しい現象で、それ以外の場合は何もしないだろう。最初の値がV Cに変更してからAに変更されたのであれば、その後、CAS操作の他のスレッドがまだ成功することができますが、これは正しいですか?Vの値が変更されたため、それは、明らかに間違っています。それでは、どのようナイジェリアを解決するには?変更プロセスは、「A-B-A」は、「1A-2B-3A」となるからであるように、実際には、変数は、バージョン番号、毎回更新された変数についてのバージョン番号プラスワンの前に添加することができます。
4、要約
Javaでは、CASの使用はでJava 1.8、アトミックオブジェクトが作成など、非常に広範囲でありConcurrentHashMap
且つRxJavaマルチスレッド変数同期は、CASによって達成されます。
[参考文献]
同期アルゴリズムとCAS(比較とスワップ)ロックフリーアルゴリズムをノンブロッキング