Javaの「volatile」は、「synchronized の軽量版」という認識しか持っておらず、あまり理解できていない気がしたので、ちょっと調べてみた。
マルチスレッド処理において、フィールド変数に volatile を付けると、各スレッドはその変数を参照する際、スレッド固有の領域ではなく、常に最新の値を見に行く。つまり、各スレッドが古い値を参照してしまうことはなくなる。
synchronized と何が違うのかと言うと、synchronized はロックを取得し、処理中は他のスレッドをブロックするが、volatile はそれはしない。単に、スレッドが対象のフィールド変数を参照する時に、常に最新の値を見れるようになるだけ。これをカッコよく (難しく) 言うと、volatile は「可視性」は持っているが、「アトミック性」は持っていない、ということになるとのこと。
Javaの理論と実践: volatile を扱う
なので、複数スレッドからアクセスされるクラスにおいて、例えば以下のように、あるフィールド変数の値を加算して、加算した結果が特定の数値だったら特別な処理を行いたい場合、synchronized だと実現できるが、volatile では実現できない。
volatile int counter = 0; void updateCounter { counter++; if (counter == 100) { // ★ doSomething(); } }
というのは、あるスレッドが counter を加算して counter が 100 になって ★ の箇所に入ろうとしたところに、別スレッドが counter を加算して counter が 101 になってしまう可能性があるからである。そうなると、本来実行したかった doSomething() が実行されない。
こんな感じで volatile は扱うのがちょっと難しいので、基本は synchronized を使い、それだと性能が出ない場合などに volatile を検討するくらいで良いのではないかと考えている。ちなみに、volatile の使いどころについては、以下のページが分かりやすい。
https://relearn-java.com/multithread/#title-how-to-use-volatile
上記によると、volatile の出番はあまりなさそうに思える。