- - PR -
ダブルチェックロッキングについて
1
投稿者 | 投稿内容 | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2006-06-09 09:06
JAVAではダブルチェックロッキングを行うとスレッドセーフではなくなる
という記事を読みまして疑問があります。 いろいろなページを調べてみると、ほとんどシングルトンでインスタンス生成を対象にダブルチェックロッキングはだめだとかかれているのですが、インスタンス生成ではない、例えば以下のようなコードでもだめなのでしょうか? class AnalyzerSingleton { // 解析をするクラス(あらかじめ作っておく) private static final Analyzer analyze = new Analyzer(); // 解析を行った唯一のanalyzerを返す public static getAnalyzer() { if (analyzer.doneAnalyze() == false) { synchronized(mutex) { if (analyzer.doneAnalyze() == false) { // 解析する analyzer.doAnalyze(); // 解析したことを示すためtrueに analyzer.setDoneAnalyze(true); } } } return analyze; } } なんとなく上記のようなコードであれば、doneAnalyzeメソッドがtrueになっている=解析済みが保障できるような気がするのですが、だめなのでしょうか? また、単純にプリミティブ型で if (flag == false) { synchronized(mutex) { if (flag == false) { // 任意の処理 flag = true; } } } とやるのも問題ない感じがします。 効率を考えればダブルチェックしたいのですが、真相がよくわかりません。 どなたかご教授願います。 | ||||||||||||||||||||
|
投稿日時: 2006-06-09 09:14
こちらをどうぞ。
http://www-06.ibm.com/jp/developerworks/java/020726/j_j-dcl.html | ||||||||||||||||||||
|
投稿日時: 2006-06-09 10:07
リンク先に書いてなかったみたいなので…。
どのバージョンからか分かりませんが、ダブルチェックを仕込んでおいても、 安全性のため?コンパイラが最適化と称してロジックを削り取ってしまうそうです。 いずれにせよ、安全なのはstaticフィールド初期化での生成ですね。 | ||||||||||||||||||||
|
投稿日時: 2006-06-10 00:13
Javaにおけるダブルチェックドロッキングについては、
問題の本質はJavaのメモリモデルにあったはずです。 それはインスタンス生成でもそうでなくても、 要するにメモリ上のある値(つまるところ共有アクセス可能なフィールド値) を参照する際、スレッドAとスレッドBでは同じ値が見えるとは 限らないというところに尽きると思います。 (私の理解が間違っているようであれば識者の方、指摘願います。) 通常ではスレッドAであるフィールドの値を変更したとしても、 スレッドBでは古いままの値が見えることがある。 しかし、synchronizedで同期をとった場合や、volatileフィールドの場合は 「メモリが同期化」されるため、現時点で正しい値が見える。 ダブルチェックドロッキングの場合では、同期を取る前のチェックが 見ている値はメモリの同期化がされる前の値なので 実際に初期化が済んでいたとしても、まだ初期化が終わっていないように 見えてしまい、多重に初期化が行われる可能性を否定できない。 そういう話だったかと思います。 チェック対象となるフィールド(または今回の例のようにメソッド)が 同期化されていれば、その心配はないですが、そもそもダブルチェックドロッキングが 同期処理によるパフォーマンスを軽減させる目的でやっているのですから、 正しくチェックするために同期処理を行うというのでは本末転倒、 ナンセンスだということだったと思います。 | ||||||||||||||||||||
|
投稿日時: 2006-06-10 01:42
この問題って、要は、hoge = new Hoge();
とやった場合に、メモリモデルの関係上、 hogeにHogeのインスタンスが設定されてから、 Hogeのコンストラクタが実行されるって話ですよね。 で、変数hogeでnullチェックを行っているため、 コンストラクタが2回実行される可能性があるという内容だったと思います。 nullチェックの対象はhogeのままとし、 Hoge piyo = new Hoge(); hoge = piyo; ってやればどうなるのかな。。。と思いました。 こういう問題が指摘されている以上、 ・アトミックかつ遅延バインドが許されない場合 staticフィールドに直接設定 ・アトミックかつ遅延バインドが許される場合 getInstanceメソッドそのものにsynchronizedをつける ・そこまで気にしない場合 ダブルチェックロッキングを実装 って感じで使い分けています。 ちょっと重たい初期化処理が2回実行されるくらいなら気にしませんが、 絶対1回しか行ってはいけない処理なら、正しく実装すべきかなと思います。 | ||||||||||||||||||||
|
投稿日時: 2006-06-10 03:13
tak3 さんが示された記事の「double-checked locking: その2」の項に書いてありますが、
は、最適化されて、
となるので、同じ問題に陥ってしまいます。 上記の項に書いてあるのですが、
結局のところ、Java においては、
ということで、使用しない方がよいと思います。 [ メッセージ編集済み 編集者: NULL 編集日時 2006-06-10 03:17 ] | ||||||||||||||||||||
|
投稿日時: 2006-06-10 23:16
setDoneAnalyze辺りの実装がどんなのか分かりませんが、これは大丈夫な気もします。 ※少なくとも、いわゆる「ダブルチェックロッキングが破綻する理由」だけ見ればですが。 あ、でも、やっぱり最適化によってメソッド間でも同じ問題が起こる可能性があるのかな? ちょっと分かりません。
こっちは駄目な気がします。 まずflagをvolatileにするなどして必ず処理後にtrueになるようにする必要があると思いますが、 ・多くのJVMでは、順序の一貫性について、volatile が正しく実装されません。 だそうなのでなんか引っかかりそうな気もします。 私はjava全然詳しく無いので、頓珍漢なこといってる可能性があります、あしからず。 javaでのvolatileやメモリモデルの仕様とかもきちんと知らないし確認もして無いです、ごめんなさい。 --追記 怪しいことは(はっきり分からないことは)やらない方が無難だとは思います。 [ メッセージ編集済み 編集者: なちゃ 編集日時 2006-06-10 23:18 ] |
1