西海岸より

つらつらざつざつと

複数スレッドからアクセスされる変数の同期化

先日から、噂の良書「Java並行処理プログラミング」をお借りしてマルチスレッドを勉強中。

以下のコードはスレッドセーフではないけど、それはどこか?

public class UnThreadSafe{
    private static boolean loopFlag = true;
    private static String message = "Default";
    private static long count = 0;

    private static class Worker extends Thread {
        public void run(){
            while(loopFlag){
                count++;
            }
            System.out.println("message: " + message);
        }
    }

    public static void main(String[] args) throws Exception{
        new Worker().start();
        Thread.sleep(100);
        message = "Done";
        loopFlag = false;
    }
}
  • (期待される)実行結果
message: Done

通常は上記のような結果が期待されるけど、実際にはこの動作になるとは限らない。
mainメソッド側のスレッドが書き込んだmessageとloopFlagは、workerスレッド側でいつ、どの順序で読み込むかは保証されていないため、loopFlagだけが先に読み込まれて"message: DEFAULT"と表示されるかもしれないし、loopFlagがいつまでもtrueでループが周り続けるかもしれない。

JVMでは、マルチプロセッサー上での効率をあげるためこのような仕様であり、同期していないオブジェクトはCPUのレジスタにキャッシュすることを許しているためらしい。

解決するにはシンプルにアクセスされる変数を同期化。

    private static volatile boolean loopFlag = true;
    private static volatile String message = "Default";

volatileを付加することで、この変数を参照する際にはメインメモリを参照するようになり、書き込み、読み込みのタイミング、順序が保証されるようになる。

うーん、マルチスレッドはかなり複雑、、しかも無知でプログラムを組むと悲惨なことになりそう。

Java並行処理プログラミング」はさらっと全体読んでみたけど、「ダメな例->改善した例」と実践形式でわかりやすく書かれており、読者が納得しながら読み進められるので本当に良い本だと思う。読んでいておもしろいし。なんでこれが廃盤になったのか。

Java並行処理プログラミング ―その「基盤」と「最新API」を究める―

Java並行処理プログラミング ―その「基盤」と「最新API」を究める―

とりあえず一票入れといた。↓

復刊ドットコム: http://www.fukkan.com/fk/VoteDetail?no=46255