前回(1回目)はサーブレットを例にスレッドセーフではないコードを考えてみたが、スレッドセーフではないことを目に見えるかたちで実証するのが難しかった。
今回はJava SEの環境で動かせるクラスでスレッドセーフではないことを実証してみる。
まずは、カウントを返すクラスを用意する。
/** * スレッドセーフでない順序数生成メソッド */ public class UnsafeSequence { /** * カウンタ */ private int nextValue; /** * 重複のない数を返す */ public int getNext() { return nextValue++; } }
次に、このカウンターを使うクラスを用意する。mainを起動すると650個のスレッドが立ち上がり、各スレッドは100回カウンターを呼び出す。
public class ThreadMain { public static void main(String[] args) { UnsafeSequenceCallerThread callerThread = new UnsafeSequenceCallerThread(); for (int i = 0; i < 650; i++) { Thread thread = new Thread(callerThread); thread.start(); } } } class UnsafeSequenceCallerThread implements Runnable { UnsafeSequence unsafeSequence = new UnsafeSequence(); public void run() { for (int i = 0; i < 100; i++) { int sequence = unsafeSequence.getNext(); System.out.println(sequence); } } }
mainを実行した結果はどうなっているだろうか?出力結果を降順にソートしてみてほしい。実行環境やタイミングによって異なるが、同じ数値が2度3度と出力され、一番大きい数が「64999」ではないハズだ。私の環境では「64997」だった。
では、期待通り動かすにはどうすればよいか?一番簡単なのはgetNextメソッド自体をsynchronizedにすること。これにより、nextValueにアクセスする唯一のgetNextメソッドがロックによってガードされることになる。
ちなみに、nextValueをvolatileにしてもダメ。nextValue++の++が非アトミックな操作なので。
Java並行処理プログラミング ―その「基盤」と「最新API」を究める―
- 作者: Brian Goetz,Joshua Bloch,Doug Lea
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2006/11/22
- メディア: 単行本
- 購入: 30人 クリック: 442回
- この商品を含むブログ (176件) を見る