【Java入門】volatileとは?サンプルコードで解説!

こんにちは!フリーランスの長野です。

volatileって使ってますか?

volatileは修飾子の一つで、マルチスレッド処理で使われます

この記事では、volatile修飾子について

・volatileとは
・volatileの使い方

という基本的な内容から、

・synchronizedとの違い
・Threadについて

など応用的な内容についても解説していきます。

今回はvolatile修飾子について、使い方をわかりやすく解説します!

volatileとは?

volatileはJavaの修飾子の一つで、マルチスレッド処理で使われます。

フィールドの値は通常メモリから呼び出され、更新されたりします。

ただこれはシングルスレッドの場合で、マルチスレッドの場合はこれとは違ってきます。

スレッドが一列で順番に処理されていくことを、「シングルスレッド」と言います。

これに対して、スレッドの列が2つ以上複数あり並列に同時進行で処理されていくことを「マルチスレッド」と言います。

マルチスレッド処理の場合、それぞれのスレッドが同じフィールドの値を別々にキャッシュすることがあります。

キャッシュとはメモリとは違う場所に値を保持することで、一般的にメモリに比べて保存容量は少ないですが、高速に値の呼び出しや更新ができます。

マルチスレッドの場合、フィールドの値をメモリより高速に呼び出し・更新するためにキャッシュすることがあるのです。

ここで注意しなければならないのが、それぞれのスレッドが同じフィールドの値を別々にキャッシュすると、同じフィールド名であっても値が違ってくる危険性があるということです。

次のようなwhile文を記述し、コンパイルのあとに実行する場合を考えてみましょう。

while(flag == 0) {
    // flagが0の間行われる処理
}

このwhile文のブロック内でflagの値を操作しない場合、while文の条件式を毎回評価する必要はありません。

このような場合にwhile文はコンパイラによって以下のように最適化することが可能になります。

if(flag == 0) {
    while(1) {
        // flagが0の間行われる処理
    }
}

シングルスレッドの場合はwhile文のブロック内でflagの値を操作しない場合、このように最適化されても問題ありません。

ただし、マルチスレッドの場合は前述のとおり他のスレッドからflagの値を操作されることがあります。

もし他のスレッドからflagの値を操作され変更された場合、最適化によってループから抜けられなくなる不具合が発生してしまうのです。

そこで、このような処理を行う場合はvolatile修飾子を使用します。

volatileの使い方

volatile修飾子の使い方をサンプルコードで確認しましょう。

public class VolatileSample {

    private static volatile int count = 0;
    //private static int count = 0;

    public static void main(String[] args) {
        new MultiThread1().start();
        new MultiThread2().start();
    }

    static class MultiThread1 extends Thread {
        public void run() {
            int val = count;
            while(count < 3) {
                if (val != count) {
                    String message = getName() + ": val = " + val + ", count = " + count;
                    System.out.println(message + " 更新");
                    val = count;
                }
            }
        }
    }

    static class MultiThread2 extends Thread {
        public void run() {
            int val = count;
            while(count < 3) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                String message = getName() + ": val = " + val + ", count = " + count;
                System.out.println(message);
                count = ++val;
            }
        }
    }
}

実行結果:

Thread-1: val = 0, count = 0
Thread-0: val = 0, count = 1 更新
Thread-1: val = 1, count = 1
Thread-0: val = 1, count = 2 更新
Thread-1: val = 2, count = 2
Thread-0: val = 2, count = 3 更新

このサンプルコードではフィールドcountの値が更新された場合に出力表示するスレッドクラスMultiThread1と約500ミリ秒ごとにcountの値を1ずつ増やすスレッドクラスMultiThread2を並列で処理しています。

MultiThread1クラスのwhile文の条件式にはフィールドcountが使われていますが、while文のブロック内ではcountの値は操作されていません。

この場合コンパイラで最適化される可能性がありますが、volatile修飾子を使って最適化を抑止しています。

以下の実行結果はvolatile修飾子なしの場合の実行結果です。

実行結果(volatile修飾子なしの場合):

Thread-1: val = 0, count = 0
Thread-1: val = 1, count = 1
Thread-1: val = 2, count = 2

このサンプルコードを実行すると、上記の実行結果を表示したあと無限にループし続けます

注意してください。

この場合volatile修飾子を使っていませんので、コンパイラで最適化され、意図した処理とは違う処理が実行されていると考えられます。

synchronizedとの違い

ここまではコンパイラの最適化の抑止について説明しました。

その他にもvolatile修飾子をフィールドにつけることで、マルチスレッド処理でメモリとキャッシュで値が違ってしまう危険性を避けるためにフィールドの値がキャッシュされるのを抑止することができます。

同じようにメモリとキャッシュで値が違ってしまう危険性を避けるためにsynchronized修飾子が使われます。

synchronized修飾子の付いたブロック内で扱うフィールドの値は、必ず元のメモリー上から呼び出し・更新されますので、キャッシュによる値の不整合を避けることができます。

volatile修飾子は簡単な値の読み書き操作で、synchronized修飾子を付けて扱うまでもない場合に使います。

synchronized修飾子ではスレッド・セーフが保証されていますが、volatile修飾子はスレッド・セーフは保証していませんので注意しましょう!

volatile修飾子とsynchronized修飾子のその他の違いについてはこちらでも詳しく解説しています。

https://www.javamex.com/tutorials/synchronization_volatile.shtml

Threadについて

マルチスレッドおよびThreadクラスについてはこちらで詳しく解説していますので、ぜひ参考にしてください。

【Java入門】マルチスレッド(Threadクラス)の使い方総まとめ
更新日 : 2019年4月29日

まとめ

ここでは、volatile修飾子について説明しました。

マルチスレッド処理でフィールドの値が他のスレッドで変更される場合、コンパイルの最適化によって無限ループにならないか注意する必要があります。

マルチスレッドの記述は最初は複雑だと感じるかもしれませんが、慣れて使いこなすことができるようにこの記事を何度も参考にして下さいね!

LINEで送る
Pocket

ITエンジニアへ転職したい方におすすめ

自分を評価してくれる企業に転職して年収を上げたい! 自分のスキルにあった独自案件を知りたい!
エンジニアは今もっとも注目されている職業の1つ。エンジニアになって年収を増やしたい方や、あなたのスキルに見合った企業へ転職したいエンジニアの方も多いですよね。

しかし、大手の転職媒体は扱う求人数が多くても、誰もが登録しているので競争率もかなり高くなっています。そのため、あなたの条件に見合った企業を見つけても転職するためには、相応の努力とスキルが必要となります。

こういった媒体では、未経験からエンジニアを目指す方やエンジニア歴2〜3年で転職を考えている方にとって、最適な転職環境とはいえません。

そこでオススメしたいのが、未経験者や若手エンジニア向けの独自案件を多く掲載している「侍ワークス」です。

侍ワークスは、独自案件を多く掲載しているだけでなく、

・応募から就業まで一貫したサポート

・就業後もアフターフォロー

といった経験の浅い方や初めてエンジニアを目指す方にも安心のフォロー体制が整っています。もちろん登録は完全無料!しかも案件を見るだけなら登録も不要です。

まずは、お気軽にどんな求人があるか見てみてください。あなたにピッタリの企業がきっと見つかりますよ! 侍ワークスの求人情報を見る

書いた人

長野 透

長野 透

熊本在住のフリープログラマ兼ライターです。C/C++/C#、Java、Python、HTML/CSS、PHPを使ってプログラミングをしています。専門は画像処理で最近は機械学習、ディープラーニングにはまっています。幅広くやってきた経験を活かしてポイントをわかりやすくお伝えしようと思います。
お問合せはこちらでも受け付けています。
[email protected]