揮発性ブールvs AtomicBoolean


244

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


16
「それぞれの制限は何か」という、より微妙な答えを探していました。たとえば、isが1つのスレッドによって設定され、1つ以上の他のスレッドによって読み取られるフラグである場合、AtomicBooleanは必要ありません。ただし、これらの回答で見たように、スレッドが複数のスレッドで変数を共有し、書き込みの可能性があり、その読み取りの結果に基づいて動作している場合、AtomicBooleanはCASタイプの非ロック操作を実行します。私は実際、かなり勉強しています。うまくいけば、他の人も恩恵を受けるでしょう。
JeffV 2010


揮発性ブール値は、競合状態を処理するために明示的な同期を必要とします。つまり、インクリメント/デクリメントカウンターやフリッピングブール値など、複数のスレッドによって共有リソースが更新(状態の変更)されるようなシナリオです。
sactiw 2017

回答:


99

彼らはただ完全に異なっています。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*オブジェクト、すべての方法は、「アトミック」であることが保証されています。

したがって、AtomicIntegerand を使用すると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、コンパイラーが最適化に少し注意するためのヒントとして機能します。


84
-1:例を挙げていますが、揮発性とAtomicxxxxの違いを実際に説明していません。
Jason S

70
問題はについてではありませんvolatile。問題はvolatile booleanvs についてAtomicBooleanです。
ドルメン2012

26
-1:ブール値について具体的に尋ねられる質問です。これは、他のデータ型と比較してユニークなケースであり、直接説明する必要があります。
John Haager、

8
@ sgp15 Java 5以降の同期と関係があります
Man of One Way

6
ブール値が多くのスレッドによって読み取られ、1つのスレッドのみによって書き込まれる場合は、それでvolatile boolean十分です。ライターも多い場合は、が必要になることがありAtomicBooleanます。
StvnBrkdll 2016年

263

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

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


1
@ksl @tetoは、1つのスレッドだけがbooleanvarを変更する場合は、を選択する必要があると説明したいと思いますvolatile boolean
znlyj 2016年

2
素晴らしい要約。
Ravindra babu 2016

56

あなたが行うことができないcompareAndSetgetAndSet揮発性のブールとアトミック操作として(もちろんしない限り、あなたはそれを同期させます)。


6
これは本当ですが、これはブール値のかなりまれな要件ではないでしょうか?
ロビン

1
@Robinは、初期化メソッドの遅延呼び出しを制御するためにそれを使用することを考えています。
Ustaman Sangat

実際、これは主要なユースケースの1つだと思います。
fool4jesus

42

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

への読み取り/書き込みのメモリ効果は、それぞれとメソッドとvolatile boolean同じです。getsetAtomicBoolean

たとえば、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度だけ通知することが保証されています(に設定された後、他のスレッドAtomicBooleanfalse再びに設定しないと仮定true)。


14

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


14
ブール(プリミティブ型と同様)アクセスは、Javaではアトミックです。読み取りと割り当ての両方。したがって、他のスレッドはブール演算を「中断」しません。
MaciejBiłas11年

1
申し訳ありませんが、これは質問にどのように答えますか?Atomic*クラスは、ラップvolatileフィールドを。
グレー

CPUは揮発性を設定する主な要因をキャッシュしていませんか?読み取った値が実際に最後に設定されたものであることを確認するには
jocull

8

揮発性ブール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 *クラスを使用して揮発性フィールドをラップすることは、オブジェクトの観点から重要な共有リソースをカプセル化する良い方法であることにも注意することが重要です。つまり、開発者はフィールドが共有されていないと想定してフィールドに対処するだけではなく、フィールド++に問題を挿入することはできません。または競合状態を導入する他のコード。


5

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

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

アトミック変数は異なり、値のアトミック変更が可能です。


4

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

一方、変数の値を設定する前に何らかのチェックを実装する必要がある場合(「trueの場合はfalseに設定する」など)は、この操作もアトミックに実行する必要があります。この場合、compareAndSetと、 AtomicBoolean、このロジックを揮発性のブール値で実装しようとする場合、getとsetの間で値が変更されていないことを確認するための同期が必要になります。


3

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

読み取り-変更-書き込み揮発性では実現できない


2
短く、サクサクしていて、要領を得ています。volatile所有者のスレッドがフィールド値を更新する機能を持ち、他のスレッドが読み取ることしかできない場合にのみ機能します。
Chaklader Asfak Arefe

3

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

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

boolean r = !myVolatileBoolean;

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

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

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


-1

どちらも同じ概念ですが、アトミックブール値では、間にCPUスイッチが発生した場合に、操作にアトミック性を提供します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.