[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[jfriends-ml 10930] Re: Doug Lea 本



村山@NETGENEです.

> > volatileやsynchronizedが,実際にネイティブコードレベルでどの
> > ように実現されているのかという話も出てません.
> メモリモデルの話までは出ていました。それより以下の部分については
> "in Java"の範疇を逸脱するし、モデル化という抽象を行ったのに具象に
> 戻ることになるので、無くてもよいと思います。

「マルチスレッド」ならば不要かもしれませんが,「並列処理」は高速化技術
なので,実行性能を意識しないわけにはいかないんです.擬似共有なんてまさに
それ.これはハードウエアの知識なしには理解できません.それに,そんなに
難しくもないですしね.

そもそも「実装から独立している抽象的なモデル」っての自体が,ほとんど
幻想だと思ってます.XML SchemaとかDOMの話なんかを見た後だと,心底そう
思います.DOMなんて酷いもんですよ.

JMMにしてみても,ハードウエアのことを知っていなければああいうモデルには
なりません.あれはほとんど共有メモリ並列マシンそのものです.

> 「挙動を変化させずに」とは言ってなく、「外部から見た振る舞いを保ったま
> ま」と書籍[リファクタリング」では言っていたと思います。「挙動を変化さ
> せずに」では外部・内部の視点がぼやけてしまいます。
そうですね.これは不適切な表現でした.

> アルゴリズムの変更は外部的振る舞いが変わらないのでリファクタリングの範
> 疇でしょう。外部的振る舞いが変わってはアルゴリズムの変更というより仕様
> の変更でしょう。
いや,これは違うと思います.

例えば,高速化を目的にバブルソートをクイックソートに変更するような場合,
「外部的振る舞いが変わらない」
というのは事実ですが,目的が異なります.

リファクタリングは可読性や再利用性などを向上させるための(平たく言えば
「綺麗に書き直す」ための)もので,ソートアルゴリズムの変更はこれには
当たらないでしょう.同期処理の変更というのは,これに近いものです.

> 書籍「リファクタリング」ではテストによってバグの混入を極力防ごうと
> していますが、マルチスレッド環境ではテスト以外の手法を用いてバグ混入を
> 防ぐことが必要とされます。
多分,それを可能にする方法は知られていません.

並列プログラミング自体が,そこまで大系づけられていないんじゃないかと
思います.

> テストではバグのないことは保証できません。たまたまテストしたケースで
> バグが見つからなかったことしか言えません。また、完全なテストをするには
> 無限に近い組み合わせを網羅せねばならず現実的ではありません。
確かに保証はできません.

ただ,並列においてはこの問題の桁が違うんです.
テストが「不十分」ではなく,ほとんど全く作成できません.

それこそ,double-cheked-lockingイディオムなんて,なんらかのテストでバグを
表面化させるのは事実上不可能です.そこにどういうバグがあるか分かっている
にも関わらず,バグを表面化させることさえ不可能なんです.ましてバグがどこに
あって,それがどんなバグか分からない時にはテストしようがありません.
それどころか,このバグによって実際に悪影響を受けた経験のある人だって,
一体どれだけいることやら.

またある種のバグは,特定ハードウエアと特定JavaVMの組み合わせでないと
表面化しなかったりします.これも事実上テスト不可能です.
#そういうものに潜伏しているバグが,ある日突然表面化すると
#「相性問題」と呼ばれるようになったりする.

理論的に,形式的記述かなにかを使ってできるのなら話は別ですが,
多分実用化は先の話でしょう.

> 「Javaスレッドプログラミング」で抜けている重要な部分や、間違っている部
だいたい示したつもりなんですが.

何よりも「並列処理が高速化技術だ」ってことさえも,明示されてなかったのでは?

> 書籍にも、リスト構造における同期の例が確か載っていました。

例えば,HashtableとConcurrentHashMapのような感じですか?

この場合は本来セマンティクス的には非同期で実現してもおかしくないものを
同期しているだけなんですけどね.だから非同期(半非同期?)に変更するのは
変な話ではないです.そのまま動くことを保証するのは難しそうですが.

話をListに戻して,この場合に全体にかけるロックをセル単位にかけるように
変更するというような変更を考えたとして,これは当然複数の読み出しや書き
込みを同時並行的に動作することを想定しているわけですよね?


普通はこういうことはやりませんが,あくまで一例として,

例えば,Iteratorみたいな処理を行うために,

synchronized( list ){

  for( ..... ){
     Iterator を使った処理;
  }
}

というのをやっていれば,ロックをセル単位で取るListに変更した時点で
破綻します.外部から見た動作を同じにするためには,セル単位でロックを
取ると同時にList全体にもロックをかけるという工夫がいります.

こういうのも,やろうと思えばできなくはないですけどね.かえって汚く
なるので,それこそリファクタリングとは言えないでしょう.

とするとおそらく,今度はこの部分で「Iteratorを使う」ということに変更が
必要になります.その際には,おそらくセマンティクスが変化します.たしか
ConcurrentHashMapもそうです.

ConcurrentHashMapのIteratorは"fail-fast"ではなく,"Weakly consistent"に
なってます.変更が加えられてもエラーにはならずそのまま動く.便利と言えば
便利ですが,一般にはこれが容認できるとは限りません.容認できない場合は
例外処理や,それを処理するプログラムのセマンティクスなどのレベルから変更
が必要になるでしょう.


などなど.変更は最終的にプログラム全体に及びかねません.

"fail-fast"から"Weakly consistent"への変更が象徴的です.