web-dev-qa-db-ja.com

揮発性ブール値とアトミックブール値

揮発性ブール値では達成できないAtomicBooleanの機能

214
JeffV

それらはまったく異なります。 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*オブジェクトを使用すると、すべてのメソッドが「アトミック」であることが保証されます。

したがって、AtomicIntegergetAndAdd(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は、コンパイラーが最適化にもう少し注意を払うためのヒントとして機能します。

86
Cephalopod

上記のフィールドが所有者スレッドによってのみ更新され、値が他のスレッドによってのみ読み取られる場合、揮発性フィールドを使用します。これは、多くのオブザーバーが存在するがパブリッシャーが1つだけのパブリッシュ/サブスクライブシナリオと考えることができます。ただし、これらのオブザーバーがフィールドの値に基づいて何らかのロジックを実行し、新しい値をプッシュバックする必要がある場合は、Atomic * varsまたはロックまたは同期ブロックを使用します。多くの並行シナリオでは、値を取得して別の値と比較し、必要に応じて更新するために要約されます。したがって、Atomic *クラスに存在するcompareAndSetおよびgetAndSetメソッドです。

Java.util.concurrent.atomic パッケージのJavaDocsをチェックして、Atomicクラスのリストとそれらがどのように機能するかについての優れた説明を確認してくださいロックまたは同期ブロック)

244
teto

compareAndSetgetAndSetをvolatileブール値を使用したアトミック操作として実行することはできません(もちろん同期しない限り)。

51
nanda

AtomicBooleanには、synchronizedブロックを使用せずに、アトミックに複合操作を実行するメソッドがあります。一方、volatile booleanは、synchronizedブロック内で実行された場合にのみ複合操作を実行できます。

volatile booleanへの読み取り/書き込みのメモリ効果は、それぞれgetsetおよび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();
}

リスナーに一度だけ通知することが保証されます(AtomicBooleanに設定された後、他のスレッドがfalsetrueに再び設定しないと仮定します)。

40
Nam San

volatileキーワードは、その変数を共有するスレッド間の発生前の関係を保証します。そのブール変数にアクセスしている間、2つ以上のスレッドが互いに割り込まないことを保証しません。

14
dhblah

クラスレベルの変数にアクセスするスレッドが複数ある場合、各スレッドはその変数のコピーをスレッドローカルキャッシュに保持できます。

変数を揮発性にすると、スレッドがスレッドローカルキャッシュに変数のコピーを保持できなくなります。

アトミック変数は異なり、値をアトミックに変更できます。

5
Amol Gaikwad

揮発性ブール値と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 *クラスを使用してvolatileフィールドをラップすることは、オブジェクトの観点から重要な共有リソースをカプセル化するための良い方法であることに注意することも重要です。これは、フィールドが共有されていない可能性があると仮定すると、開発者はフィールドを扱うことができないことを意味します。または競合状態を導入する他のコード。

5
Gray

ブールプリミティブ型は書き込みおよび読み取り操作に対してアトミックであり、volatileは発生前の原則を保証します。したがって、単純なget()およびset()が必要な場合、AtomicBooleanは必要ありません。

一方、変数の値を設定する前にいくつかのチェックを実装する必要がある場合は、 「trueの場合falseに設定する」場合、この操作もアトミックに行う必要があります。この場合、compomicAndSetとAtomicBooleanが提供する他のメソッドを使用します。 getとsetの間で値が変更されていないことを確認してください。

4
user2660000

IDIOMを覚えておいてください-

READ-MODIFY- WRITEこれはvolatileでは達成できません

3
MoveFast

ブール値を変更するスレッドが1つしかない場合は、揮発性ブール値を使用できます(通常、これを実行して、スレッドのメインループでチェックされるstop変数を定義します)。

ただし、ブール値を変更する複数のスレッドがある場合は、AtomicBooleanを使用する必要があります。そうでない場合、次のコードは安全ではありません。

boolean r = !myVolatileBoolean;

この操作は2つのステップで実行されます。

  1. ブール値が読み取られます。
  2. ブール値が書き込まれます。

他のスレッドが#12#の間の値を変更すると、間違った結果になる可能性があります。 AtomicBooleanメソッドは、ステップ#1および#2をアトミックに実行することにより、この問題を回避します。

2
Thibaut D.