Javaのvolatile修飾子の使い方を現役エンジニアが解説【初心者向け】
初心者向けにJavaのvolatile修飾子の使い方について解説しています。最初に修飾子とは何か、その種類について学習します。次にvolatileの役割と使用する場面の例、書き方をサンプルコードで見ていきましょう。
テックアカデミーマガジンは受講者数No.1のプログラミングスクール「テックアカデミー」が運営。初心者向けにプロが解説した記事を公開中。現役エンジニアの方はこちらをご覧ください。 ※ アンケートモニター提供元:GMOリサーチ株式会社 調査期間:2021年8月12日~8月16日 調査対象:2020年8月以降にプログラミングスクールを受講した18~80歳の男女1,000名 調査手法:インターネット調査
Javaのvolatile修飾子の使い方について解説します。
実際にプログラムを書いて説明しているので、ぜひ理解しておきましょう。
そもそもJavaについてよく分からないという方は、Javaとは何なのか解説した記事を読むとさらに理解が深まります。
なお本記事は、TechAcademyのオンラインブートキャンプJava講座の内容をもとに作成しています。
今回は、Javaに関する内容だね!
どういう内容でしょうか?
volatile修飾子の使い方について詳しく説明していくね!
お願いします!
Javaの修飾子
Javaには以下のような修飾子があります。
- public
- protected
- private
- abstract
- fina
- strictfp
- static
- transient
- volatile
- native
- synchronized
このうちpublic、protected、privateの3つはアクセス修飾子と呼ばれ、それ以外は単に修飾子と呼ばれます。
volatileとは
volatileはjavaの修飾子の一つで、フィールドに対して付加できるものです。マルチスレッド処理で使われます。マルチスレッド処理とは複数の処理が同時に発生する構造になっている処理です。
volatileの書き方
フィールドの直前に付加して記述します。
実際に書いてみよう
volatile修飾子が必要になるのは下記のようなケースです。ClassB中のrunメソッドと、ClassC中のrunメソッドを同時に実行します。両方のクラスはaという共通のstaticフィールドを参照しています。
ClassCのメソッドは1秒間隔で実行され、実行する度にaの値をインクリメントします。ClassBのメソッドはaの値に変更がなされた場合に、その値を出力するようにしています。
package test; public class SampleA { // 二つの処理から参照される値 private static volatile int a = 0; public static void main(String[] args) { new ClassB().start(); new ClassC().start(); } static class ClassB extends Thread { @Override public void run() { //aの内容をローカル変数へ退避 int b = a; //bの値が5未満の場合、繰り返す while (b < 5) { //aの値の退避~繰り返し処理をしている間に //aの値が変更されたら画面に出力する。 if (b != a) { System.out.println("[ClassB] aの値 :" + a); b = a; } } } } static class ClassC extends Thread { @Override public void run() { //aの内容をローカル変数へ退避 int c = a; while (a < 5) { System.out.println("[ClassC] aの値を変更:" + (c + 1)); //インクリメントしたcの値(つまり自身の値をaへ代入) a = ++c; try { //当スレッドが1秒間隔で実行されるようにする Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
実行結果は
[ClassC] aの値を変更:1 [ClassB] aの値 :1 [ClassC] aの値を変更:2 [ClassB] aの値 :2 [ClassC] aの値を変更:3 [ClassB] aの値 :3 [ClassC] aの値を変更:4 [ClassB] aの値 :4 [ClassC] aの値を変更:5 [ClassB] aの値 :5
となります。
ClassCによりaの値が変更される度に、ClassBからaの値が出力されていることが分かります。ここでフィールドaにvolatile修飾子をつけないようにすると例えば下記のような結果になります
[ClassC] aの値を変更:1 [ClassB] aの値 :1 [ClassC] aの値を変更:2 [ClassC] aの値を変更:3 [ClassC] aの値を変更:4 [ClassC] aの値を変更:5
[ClassC]による値の変更があった後、[ClassB]からの値の出力がありません。つまりClassBで、aの値が変更されたということが識別できていないということです。
スレッドは、値へのアクセスを高速化する為にフィールドの値をフィールド独自のメモリ領域にキャッシュします。マルチスレッド処理では同じフィールドの値を別の領域へキャッシュすることになるので、JVMによるキャッシュのタイミングによってはそれぞれの領域の値が違ってしまう可能性があります。
volatile修飾子はその値が他のスレッドから更新されることがあるということを示すことで、JVMがキャッシュ値を更新するのではなく元の変数の値を更新するようになるのです。
監修してくれたメンター
長屋雅美
独立系SIerで7年勤務後、現在はフリーのエンジニアとして自宅をオフィスとして活動しています。 |
内容分かりやすくて良かったです!
ゆかりちゃんも分からないことがあったら質問してね!
分かりました。ありがとうございます!
TechAcademyでは、初心者でもJavaやServletの技術を使ってWebアプリケーション開発を習得できるオンラインブートキャンプJava講座を開催しています。
挫折しない学習方法を知れる説明動画や、現役エンジニアとのビデオ通話とチャットサポート、学習用カリキュラムを体験できる無料体験も実施しているので、ぜひ参加してみてください。