- PR -

ダブルチェックロッキングについて

1
投稿者投稿内容
Tol
常連さん
会議室デビュー日: 2004/07/16
投稿数: 27
投稿日時: 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;
}
}
}
とやるのも問題ない感じがします。

効率を考えればダブルチェックしたいのですが、真相がよくわかりません。
どなたかご教授願います。

tak3
ベテラン
会議室デビュー日: 2004/04/15
投稿数: 80
お住まい・勤務地: 菜の花・銀杏
投稿日時: 2006-06-09 09:14
こちらをどうぞ。

http://www-06.ibm.com/jp/developerworks/java/020726/j_j-dcl.html
mio
ぬし
会議室デビュー日: 2005/08/25
投稿数: 734
お住まい・勤務地: 神奈川県
投稿日時: 2006-06-09 10:07
リンク先に書いてなかったみたいなので…。
どのバージョンからか分かりませんが、ダブルチェックを仕込んでおいても、
安全性のため?コンパイラが最適化と称してロジックを削り取ってしまうそうです。

いずれにせよ、安全なのはstaticフィールド初期化での生成ですね。
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2006-06-10 00:13
Javaにおけるダブルチェックドロッキングについては、
問題の本質はJavaのメモリモデルにあったはずです。

それはインスタンス生成でもそうでなくても、
要するにメモリ上のある値(つまるところ共有アクセス可能なフィールド値)
を参照する際、スレッドAとスレッドBでは同じ値が見えるとは
限らないというところに尽きると思います。
(私の理解が間違っているようであれば識者の方、指摘願います。)

通常ではスレッドAであるフィールドの値を変更したとしても、
スレッドBでは古いままの値が見えることがある。
しかし、synchronizedで同期をとった場合や、volatileフィールドの場合は
「メモリが同期化」されるため、現時点で正しい値が見える。
ダブルチェックドロッキングの場合では、同期を取る前のチェックが
見ている値はメモリの同期化がされる前の値なので
実際に初期化が済んでいたとしても、まだ初期化が終わっていないように
見えてしまい、多重に初期化が行われる可能性を否定できない。
そういう話だったかと思います。

チェック対象となるフィールド(または今回の例のようにメソッド)が
同期化されていれば、その心配はないですが、そもそもダブルチェックドロッキングが
同期処理によるパフォーマンスを軽減させる目的でやっているのですから、
正しくチェックするために同期処理を行うというのでは本末転倒、
ナンセンスだということだったと思います。
かつのり
ぬし
会議室デビュー日: 2004/03/18
投稿数: 2015
お住まい・勤務地: 札幌
投稿日時: 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回しか行ってはいけない処理なら、正しく実装すべきかなと思います。
NULL
会議室デビュー日: 2005/09/28
投稿数: 4
投稿日時: 2006-06-10 03:13
引用:

かつのりさんの書き込み (2006-06-10 01:42) より:
nullチェックの対象はhogeのままとし、
Hoge piyo = new Hoge();
hoge = piyo;
ってやればどうなるのかな。。。と思いました。


tak3 さんが示された記事の「double-checked locking: その2」の項に書いてありますが、

コード:
Hoge piyo = new Hoge();

hoge = piyo;


は、最適化されて、

コード:
hoge = new Hoge();


となるので、同じ問題に陥ってしまいます。
上記の項に書いてあるのですが、

引用:
Java言語仕様 (JLS) では、synchronized ブロック内のコードが synchronized ブロックの外側に移動しないことが求められています。しかし、synchronized ブロックに含まれていないコードを synchronized ブロックの 中に 移動することはできない、とは言っていません。


結局のところ、Java においては、

引用:
http://www-06.ibm.com/jp/developerworks/java/020726/j_j-dcl.html#6より:
double-checked lockingは (いかなる形式のものであっても)、どのJVM実装でも動作するとは保証できないため、使用すべきではありません。


ということで、使用しない方がよいと思います。

[ メッセージ編集済み 編集者: NULL 編集日時 2006-06-10 03:17 ]
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2006-06-10 23:16
引用:

lamさんの書き込み (2006-06-09 09:06) より:
コード:

if (analyzer.doneAnalyze() == false) {
synchronized(mutex) {
if (analyzer.doneAnalyze() == false) {
// 解析する
analyzer.doAnalyze();
// 解析したことを示すためtrueに
analyzer.setDoneAnalyze(true);
}
}
}
return analyze;


なんとなく上記のようなコードであれば、doneAnalyzeメソッドがtrueになっている=解析済みが保障できるような気がするのですが、だめなのでしょうか?


setDoneAnalyze辺りの実装がどんなのか分かりませんが、これは大丈夫な気もします。
※少なくとも、いわゆる「ダブルチェックロッキングが破綻する理由」だけ見ればですが。

あ、でも、やっぱり最適化によってメソッド間でも同じ問題が起こる可能性があるのかな?
ちょっと分かりません。

引用:

また、単純にプリミティブ型で
コード:

if (flag == false) {
synchronized(mutex) {
if (flag == false) {
// 任意の処理
flag = true;
}
}
}


とやるのも問題ない感じがします。


こっちは駄目な気がします。
まずflagをvolatileにするなどして必ず処理後にtrueになるようにする必要があると思いますが、
・多くのJVMでは、順序の一貫性について、volatile が正しく実装されません。
だそうなのでなんか引っかかりそうな気もします。

私はjava全然詳しく無いので、頓珍漢なこといってる可能性があります、あしからず。
javaでのvolatileやメモリモデルの仕様とかもきちんと知らないし確認もして無いです、ごめんなさい。

--追記
怪しいことは(はっきり分からないことは)やらない方が無難だとは思います。

[ メッセージ編集済み 編集者: なちゃ 編集日時 2006-06-10 23:18 ]
1

スキルアップ/キャリアアップ(JOB@IT)