スレッドセーフを簡単にまとめました。

スレッドセーフとは

複数のスレッドからアクセスされたときに、
実行のタイミングがどうであっても、
呼び出し側に同期化の努力が必要なく正しく振る舞えるクラスのこと。

スレッドセーフを考えるときに一番問題なのは、
オブジェクトのステート(フィールド)へのアクセス。

複数のスレッドによる無秩序なアクセスがあっても、
正しい状態を維持し続けられるようにクラスを作ることが
すなわち、スレッドセーフなクラスを作成することになる。

このとき、synchronizedや明示的ロックやアトミック変数などを利用する。

スレッドセーフな例

サーブレット

ステートレスなサーブレットは共有するステートがそもそもないので、
スレッドセーフである。

サーブレットのポリシーに従えば、
サーブレット自体にフィールドを持たないようになっている。

このためリクエストのたびにスレッドが生成されるサーブレットでも
スレッドセーフとなる。

スレッドセーフではない例

競り合い状態

状態をチェックして次の行動を決める場合は、
スレッドセーフにならない場合が多いので要注意。

public class RaceCondition {   
    private static RaceCondition instatnce = null;
     
    public static RaceCondition getInstance(){
        if(instatnce == null)
            instatnce = new RaceCondition();
        return instatnce;
    }
}

この例は、マルチスレッドに対応していないシングルトンパターン。

一見すると正しく動く(インスタンスが1つに保たれる)気がするが、
スレッドセーフではない典型的な例。

次の順で実行されると、簡単にインスタンスが複数作れる。

1 2 3 4
スレッドA instatnce == null→true instatnce = new RaceCondition();
スレッドB instatnce == null→true instatnce = new RaceCondition();

このように状態をチェックして
次のアクションに移るまでのタイムラグがあると、
そのすきに他のスレッドがアクションが起こる前の状態をチェックして
不正な状態に持ち込まれてしまうことになる。

この例を修正するには、
メソッドにsynchronizedを指定すればよい。(他にも方法はある)

そうすると、必ず状態をチェックしてアクションした後でなければ
次のアクセスが実行されないので、
この例では正しさが保たれることになる。

PR