K.Maebashi's BBS

ご自由に書き込んでください。雑談も可。
テスト書き込みの類はテスト用掲示板にどうぞ

[日付順表示] [日付順インデックス] [スレッド順インデックス]

新規投稿 | 開設者ホームページへ戻る | ヘルプ

[406] synchronizedメソッドの"変なこと"
投稿者:sgi
2007/02/20 02:13:25

こんにちは。はじめまして。 楽しく拝読させて頂いております。 「オブジェクト指向再入門/なぜわからなくなってしまうのか?」の中の記述に判らないところがありましたのでご質問させてください。 ~引用ここから~ ところで「Javaの格言」という本では、 Pointのx, yをprivateにするメリットとして、マルチスレッド時の排他制御を挙げています。しかし、getX(), getY(), setX(), setY() を作ってそれぞれをsynchronizedにしたところで、この仕様ではマルチスレッドには対応できません。ちょっと考えてみればわかるように、 getとsetが分かれていたり、setのXとYが分かれていたりしていたのでは、複数のスレッドで実行されたら結局変なことが起きます。 ~引用ここまで~ synchronizedメソッドをコールすると、コールしたスレッドがインスタンス(メソッドのレシーバ)のモニタを取得するために、別スレッドが当該インスタンスのsynchronizedメソッドを呼び出しても安全(スレッドセーフ)であると認識しております。 上記の引用のなかの"変なこと"というのが、私の認識であるところの"スレッドセーフ"でないことを指すのか、または別のことを指すのか判りませんでした。 もしよろしければ、可能性として起こりうる"変なこと"をご教示願えませんでしょうか? よろしくお願いします。
[この投稿を含むスレッドを表示] [この投稿を削除]
[407] Re:synchronizedメソッドの“変なこと”
投稿者:(ぱ)
2007/02/20 02:13:25

>こんにちは。はじめまして。 はじめまして。書き込みありがとうございます。 >もしよろしければ、可能性として起こりうる"変なこと"をご教示願えませんでしょうか? >getとsetが分かれていたり、setのXとYが分かれていたりしていたのでは、 というのが一応ヒントというか根拠のつもりです。 簡単な方から。 ◎setのXとYが分かれていたりしていたのでは… 現在(100, 100)であるPointを、(200, 200)に移動させたかったとしましょう。 そのために、 p.setX(200); p.setY(200); と書くと、setX()してからsetY()するまでの間、一時的に(200, 100)という状態になり、 この状態が他スレッドから見えてしまいます。 ◎getとsetが分かれていたり… ふたつのスレッドA, Bが以下のようにxをインクリメントしようとすると、 x = p.getX(); // スレッドA ←(1) x = p.getX(); // スレッドB ←(2) p.setX(x+1); // スレッドA ←(3) p.setX(x+1); // スレッドB ←(4) (3)でスレッドAがインクリメントした結果が(4)で上書きされてしまい、 ふたつのスレッドがひとつずつインクリメントしているはずなのに、 結局xは1しか増えないことになります。 ひとつめの問題は仕様だと言い張ることも可能かもしれませんが、 ふたつめの問題はおそらく致命的でしょう。 結局、マルチスレッドで正しくプログラムを動かしたいのであれば、 Pointのような低レベルなクラスで、個々のメソッドに機械的にsynchronizedを つけても意味がなく、アプリケーションのレベルで対処しなければなりません。 この手の問題は標準のクラスライブラリにもあって、古いコレクションクラス (Vectorなど)は、メソッドごとにちまちまとsynchronizedを付けていますが、 現実問題としてこれは無意味であり、後継のArrayListなどでは外されたわけです。 // ループが回っている間に別スレッドでremove()とかされると例外が発生する size = v.size(); for (int i = 0; i < size; i++) { Object o = v.get(i); } でも今Googleしてみたら、「シングルスレッドでよいときは効率向上のために ArrayListを使い、スレッドセーフにしたければVectorを使うべし」と解説している ページの多いこと… あんまり理解されてないことなんですかねえ。
[この投稿を含むスレッドを表示] [この投稿を削除]
[410] Re:synchronizedメソッドの“変なこと”
投稿者:本多
2007/02/20 02:13:25

>◎getとsetが分かれていたり… ~中略~ >ふたつのスレッドA, Bが以下のようにxをインクリメントしようとすると、 > x = p.getX(); // スレッドA ←(1) > x = p.getX(); // スレッドB ←(2) > p.setX(x+1); // スレッドA ←(3) > p.setX(x+1); // スレッドB ←(4) ~中略~ >結局、マルチスレッドで正しくプログラムを動かしたいのであれば、 >Pointのような低レベルなクラスで、個々のメソッドに機械的にsynchronizedを >つけても意味がなく、アプリケーションのレベルで対処しなければなりません。 ~中略~ >あんまり理解されてないことなんですかねえ。 こういうのって、マルチスレッド プログラムで再現性の低いバグで悩んだ経験がないとなかなか思いつかないことかもしれませんね。 ずいぶん昔、私もマルチスレッドではないのですが、複数のCPUで一つのデバイスにアクセスするようなプログラムではまったことがあって、その原因はこんな行でした。 x |= y; xの値をCPUレジスタに読み出して、その値にyの値を加えたものを、xに書き込む...前に別のCPUからxに書き込みがあったんです。 このときは高い確率で再現できるプログラムが偶然用意できたのが幸いでしたが。
[この投稿を含むスレッドを表示] [この投稿を削除]
[413] Re:synchronizedメソッドの“変なこと”
投稿者:(ぱ)
2007/02/20 02:13:25

>ずいぶん昔、私もマルチスレッドではないのですが、複数のCPUで一つのデバイスに >アクセスするようなプログラムではまったことがあって、その原因はこんな行でした。 > > x |= y; なんかモロにマルチスレッドのように思うんですが… (^^; これぐらいミクロなレベルでバグを入れてしまうと、もうたいていはデバッグどころか 再現するのも不可能でどうしようもなくなりますから、Webアプリケーションなんかでは、 ・そもそも複数のスレッドで共有するオブジェクトを限定する。 ・それでも共有するオブジェクトは、入り口に管理用のオブジェクトを  置くとかしてちゃんと管理する。 ってことだと私は思っています。 でも、組み込みなどだとまた事情が違いそうですね。
[この投稿を含むスレッドを表示] [この投稿を削除]
[414] Re:synchronizedメソッドの“変なこと”
投稿者:本多
2007/02/20 02:13:25

>>ずいぶん昔、私もマルチスレッドではないのですが、複数のCPUで一つのデバイスに >>アクセスするようなプログラムではまったことがあって、その原因はこんな行でした。 >> x |= y; >なんかモロにマルチスレッドのように思うんですが… (^^; 全く異なる複数のCPU上で動作する2つのプロセスが同じリソースにアクセスする様なものも「マルチスレッド」と言うのかどうか、ちとわからんのですが、 >でも、組み込みなどだとまた事情が違いそうですね。 組み込みの癖に複数のCPUがあるなんていう贅沢な組み込み環境なのですが、複数のCPUがあるくせに排他制御や割り込みをあげるハードウエアがプア...というか、存在しなくて(CPUは汎用品だけど、CPU間をつなぐ部分が社内製だった)、しかたなく同時にアクセスできるメモリにフラグを立てる形で実現していたとかいう状況だったりします。 で、フラグが何種類か必要なので、ビット対応にした(だからORしてる)というなんとも間抜けな設計でスタートしていて。 そもそも通信をガシガシやる部分だったので「ほとんどの情報は共有されてる」ために「複数のスレッドで共有するオブジェクトを限定する」っていうのは意味がなくて「入り口に管理用のオブジェクトを置くとかしてちゃんと管理する」のまさに管理用のオブジェクトが、こんなのだったんですね。あのころは若かったと言い訳して...(^^;)
[この投稿を含むスレッドを表示] [この投稿を削除]