揮発性のブール値では達成できないAtomicBooleanの機能
揮発性のブール値では達成できないAtomicBooleanの機能
回答:
彼らはただ完全に異なっています。volatile
次の整数の例を考えてみます。
volatile int i = 0;
void incIBy5() {
i += 5;
}
2つのスレッドが関数を同時に呼び出す場合i
、コンパイルされたコードはこれといくらか似ているため、後で5になる可能性があります(で同期できないことを除くint
)。
void incIBy5() {
int temp;
synchronized(i) { temp = i }
synchronized(i) { i = temp + 5 }
}
変数が揮発性の場合、その変数へのすべてのアトミックアクセスは同期されますが、実際にアトミックアクセスと見なされるものは必ずしも明らかではありません。ではAtomic*
オブジェクト、すべての方法は、「アトミック」であることが保証されています。
したがって、AtomicInteger
and を使用するとgetAndAdd(int delta)
、結果は確実にになります10
。同様に、2つのスレッドが両方ともboolean
同時に変数を否定する場合、を使用すると、AtomicBoolean
後で元の値が確実にvolatile boolean
得られますが、を使用するとできません。
したがって、フィールドを変更するスレッドが複数ある場合は、それをアトミックにするか、明示的な同期を使用する必要があります。
の目的volatile
は異なります。この例を考えてみましょう
volatile boolean stop = false;
void loop() {
while (!stop) { ... }
}
void stop() { stop = true; }
実行中のスレッドloop()
と別のスレッドがを呼び出しstop()
ている場合volatile
、最初のスレッドがstopの値をキャッシュする可能性があるため、を省略すると無限ループに陥る可能性があります。ここではvolatile
、コンパイラーが最適化に少し注意するためのヒントとして機能します。
volatile
。問題はvolatile boolean
vs についてAtomicBoolean
です。
volatile boolean
十分です。ライターも多い場合は、が必要になることがありAtomicBoolean
ます。
上記のフィールドがその所有者スレッドによってのみ更新され、値が他のスレッドによってのみ読み取られる場合、私は揮発性フィールドを使用します。これは、多くのオブザーバーがあり、パブリッシャーが1つだけのパブリッシュ/サブスクライブシナリオと考えることができます。ただし、これらのオブザーバーがフィールドの値に基づいて何らかのロジックを実行し、新しい値をプッシュバックする必要がある場合は、Atomic *変数、ロック、または同期ブロックを使用します。多くの同時シナリオでは、値を取得して別の値と比較し、必要に応じて更新するために要約されます。したがって、Atomic *クラスにあるcompareAndSetメソッドとgetAndSetメソッドが存在します。
java.util.concurrent.atomicパッケージのJavaDocsをチェックして、Atomicクラスのリストとそれらがどのように機能するかについての優れた説明を確認してください(ロックなしであり、ロックや同期ブロックよりも優れていることがわかりました)
boolean
varを変更する場合は、を選択する必要があると説明したいと思いますvolatile boolean
。
あなたが行うことができないcompareAndSet
、getAndSet
揮発性のブールとアトミック操作として(もちろんしない限り、あなたはそれを同期させます)。
AtomicBoolean
synchronized
ブロックを使用せずにアトミックに複合操作を実行するメソッドがあります。一方、ブロックvolatile boolean
内で実行した場合にのみ、複合操作を実行できsynchronized
ます。
への読み取り/書き込みのメモリ効果は、それぞれとメソッドとvolatile boolean
同じです。get
set
AtomicBoolean
たとえば、compareAndSet
メソッドはアトミックに以下を実行します(synchronized
ブロックなし):
if (value == expectedValue) {
value = newValue;
return true;
} else {
return false;
}
したがって、このcompareAndSet
メソッドを使用すると、複数のスレッドから呼び出された場合でも、1回だけ実行されることが保証されたコードを記述できます。例えば:
final AtomicBoolean isJobDone = new AtomicBoolean(false);
...
if (isJobDone.compareAndSet(false, true)) {
listener.notifyJobDone();
}
リスナーに1度だけ通知することが保証されています(に設定された後、他のスレッドAtomicBoolean
がfalse
再びに設定しないと仮定true
)。
volatile
キーワードは、その変数を共有するスレッド間で発生する前の関係を保証します。そのブール変数にアクセスしている間、2つ以上のスレッドが相互に割り込まないことは保証されません。
Atomic*
クラスは、ラップvolatile
フィールドを。
揮発性ブールvs AtomicBoolean
Atomic *クラスは、同じタイプの揮発性プリミティブをラップします。ソースから:
public class AtomicLong extends Number implements java.io.Serializable {
...
private volatile long value;
...
public final long get() {
return value;
}
...
public final void set(long newValue) {
value = newValue;
}
したがって、Atomic *を取得して設定するだけの場合は、代わりにvolatileフィールドを使用することもできます。
揮発性のブール値では達成できないAtomicBooleanの機能
Atomic *クラスincrementAndGet()
はcompareAndSet()
、ロックなしで複数の操作(get / increment / set、test / set)を実装する、、その他のより高度な機能を提供するメソッドを提供します。そのため、Atomic *クラスは非常に強力です。
たとえば、複数のスレッドがを使用して次のコードを使用している場合++
、++
実際にはget、increment、およびsetであるため、競合状態が発生します。
private volatile value;
...
// race conditions here
value++;
ただし、次のコードはマルチスレッド環境でロックなしで安全に機能します。
private final AtomicLong value = new AtomicLong();
...
value.incrementAndGet();
Atomic *クラスを使用して揮発性フィールドをラップすることは、オブジェクトの観点から重要な共有リソースをカプセル化する良い方法であることにも注意することが重要です。つまり、開発者はフィールドが共有されていないと想定してフィールドに対処するだけではなく、フィールド++に問題を挿入することはできません。または競合状態を導入する他のコード。
クラスレベルの変数にアクセスするスレッドが複数ある場合、各スレッドはその変数のコピーをスレッドローカルキャッシュに保持できます。
変数を揮発性にすると、スレッドは変数のコピーをスレッドローカルキャッシュに保持できなくなります。
アトミック変数は異なり、値のアトミック変更が可能です。
ブールプリミティブ型は、書き込みおよび読み取り操作に対してアトミックです。揮発性は、前に発生する原理を保証します。したがって、単純なget()およびset()が必要な場合は、AtomicBooleanは必要ありません。
一方、変数の値を設定する前に何らかのチェックを実装する必要がある場合(「trueの場合はfalseに設定する」など)は、この操作もアトミックに実行する必要があります。この場合、compareAndSetと、 AtomicBoolean、このロジックを揮発性のブール値で実装しようとする場合、getとsetの間で値が変更されていないことを確認するための同期が必要になります。
IDIOMを覚えておいてください-
読み取り-変更-書き込み揮発性では実現できない
volatile
所有者のスレッドがフィールド値を更新する機能を持ち、他のスレッドが読み取ることしかできない場合にのみ機能します。
ブール値を変更するスレッドが1つしかない場合は、揮発性ブール値を使用できます(通常、これは、stop
は、スレッドのメインループでチェックさ変数ます)。
ただし、ブール値を変更するスレッドが複数ある場合は、を使用する必要がありAtomicBoolean
ます。そうでなければ、次のコードは安全ではありません:
boolean r = !myVolatileBoolean;
この操作は2つのステップで行われます。
他のスレッドが#1
との間の値を変更すると2#
、誤った結果になる可能性があります。AtomicBoolean
メソッドは、ステップ#1
を#2
アトミックに行うことでこの問題を回避します。