前回
から別の方向に大きく逸れてしまいましたが、別スレッドでの結果を取得する第2回です。
下記コードはThread.join()を使って他のスレッドの終わりを待って値を取得しようというサンプルです。
/**
* joinを用いて他のThreadの結果を取得するサンプル。
* 同期が行われていないので潜在的な危険を孕んでいる?
*/
public class JoinTest extends Thread {
/** return用の値 */
private int value;
/** Threadでの処理 */
@Override
public void run() {
// 実処理
// ...
this.value = 0;
}
/**
* 値取得用のメソッド。
*/
public int getValue() {
return this.value;
}
public static void main(String[] args) throws Exception {
JoinTest t = new JoinTest();
t.start();
// Threadの終了を待つ
t.join();
// 結果の取得
int ret = t.getValue();
}
}
私の理解が正しければ、このコードは正しい値を返さない可能性があるのではないでしょうか?
double-checked locking問題と同じ?
どうにもこのコード、
double-checked locking問題
と同じ匂いがするのです。
Thread.join()による待機の完了は、対象スレッドのどの処理よりも後となることは保証されています。
では、上記サンプルのように対象スレッド内でフィールドに書き出しておいて、
join()で終わりを待って値を読み込めば正しく値が読み出せるように思えます。
ところが、Thread.join()は同期されるわけではないので、この時点でメモリが同期されていることを
期待してはいけないのではないでしょうか?
受け渡し用のフィールドがvolatileでありさえすれば、問題は起こらないことでしょう。
しかし非volatileでかつ非synchronizedの場合は…?
クラス設計上の問題
wait()方式に比べた弱点は、クラスとして完結させにくい点です。
少なくとも、Thread#start()の後にThread#join()して、それから値を取得してくださいね、
なんて約束をクラスの利用者に守ってもらうというのは防衛的ではないですよね。
防衛的なクラス設計にするのであれば、getValue()メソッドの内部で完了を待機したいところです。
そうなると、Thread#join()を実行するために、自身が実行されているThreadのインスタンスが必要となります。
結局、Thread.start()までをもクラス内部に抱え込まないと安全で利便性の高いクラスにはできません…。
このように、クラス設計において、wait()方式に比べると若干面倒が多いように思えます。
InterruptedExceptionへの対応が必要な点や、同期が必要な点はwait()と変わりませんから
メリットは特に無いのではないでしょうか。Threadはリソースも多く食いますしね。
投稿日時 : 2007年8月27日 21:12