Java での変数値の設定操作は、long 型変数と double 型変数を除いてアトミックな操作であることがわかっています。つまり、変数値の単純な読み取りおよび書き込み操作を同期する必要はありません。
JVM 1.2 より前では、Java のメモリ モデル実装は常にメイン メモリから変数を読み取るため、特別な注意は必要ありませんでした。 JVM の成熟と最適化に伴い、マルチスレッド環境では volatile キーワードの使用が非常に重要になってきました。現在の Java メモリ モデルでは、スレッドはメイン メモリで直接読み書きする代わりに、変数をローカル メモリ (マシン レジスタなど) に保存できます。これにより、あるスレッドがメイン メモリ内の変数の値を変更する一方で、別のスレッドがレジスタ内の変数値のコピーを使用し続け、データの不整合が発生する可能性があります。この問題を解決するには、このプログラムのように変数を volatile (不安定) として宣言するだけで済みます。これは、この変数が不安定であり、読み取りに使用されるたびにメイン メモリに格納されることを JVM に示します。一般的に言えば
したがって、マルチタスク環境では、タスク間で共有されるフラグを volatile で変更する必要があります。
Volatile によって変更されたメンバー変数がスレッドによってアクセスされるたびに、メンバー変数の値が共有メモリから強制的に再読み取りされます。さらに、メンバー変数が変更されると、スレッドは変更された値を共有メモリに書き戻すことを強制されます。このようにして、いつでも 2 つの異なるスレッドがメンバー変数の同じ値を参照します。
Java 言語仕様には次のように記載されています。最適な速度を実現するために、スレッドは、同期されたコード ブロックに出入りするときにのみ、共有メンバー変数のプライベート コピーを保存し、共有メンバー変数の元の値と比較することができます。
このように、複数のスレッドが同時にオブジェクトと対話する場合、スレッドが共有メンバー変数の変更を適時に取得できるようにすることに注意を払う必要があります。
volatile キーワードは VM にプロンプトを表示します。VM はこのメンバー変数のプライベート コピーを保存できませんが、共有メンバー変数と直接対話する必要があります。
使用上の提案: 2 つ以上のスレッドによってアクセスされるメンバー変数には volatile を使用します。アクセスする変数が既に同期コード ブロック内にある場合、または定数の場合は使用する必要はありません。
volatile を使用すると、VM で必要なコードの最適化がブロックされ、効率が低下するため、このキーワードは必要な場合にのみ使用する必要があります。
仮想マシンの実装では、int や char などの基本型は 1 ワードの長さです。また、long と double は 2 単語の長さを占めます。一部の仮想マシン実装では、2 つの語長が 2 つのアトミックな単一語長として扱われる場合があります。
long と double が volatile で変更されていない場合、複数のスレッドが変数にアクセスすると、long 操作の全体的な非アトミック性により結果が混乱します。
例: int の場合、あるスレッドは 4 を書き込み、別のスレッドは 5 を書き込みます。最終値は 4 または 5 でなければなりません。また、long 型は乱雑な値になる可能性があります。