いつ使用しAtomicReference
ますか?
すべてのマルチスレッド プログラムでオブジェクトを作成する必要がありますか?
AtomicReference を使用する必要がある簡単な例を提供します。
いつ使用しAtomicReference
ますか?
すべてのマルチスレッド プログラムでオブジェクトを作成する必要がありますか?
AtomicReference を使用する必要がある簡単な例を提供します。
アトミック参照は、参照に対して単純なアトミック(つまり、スレッドセーフで自明ではない) 操作を行う必要があり、モニターベースの同期が適切でない設定で使用する必要があります。オブジェクトの状態が最後に確認したとおりのままである場合にのみ、特定のフィールドを確認したいとします。
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*
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;
}
}
}
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
などの他の構造を使用してください。
もう 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