#Futureパターン
実行結果を得るまでに時間がかかるメソッドがあるとする。実行結果を得るまで待つ代わりに引換券をもらう。引換券をもらうのに時間はかからない。このときの引換券をFuture役と呼ぶ。引換券をもらったスレッドは、あとでFuture役を使って実行結果を受け取りに行く。もし実行結果ができていれば、すぐにそれをもらい、できていなければできるまで待つことになる。
(コード全体は本書を参照のこと)
public class Main {
public static void main(String[] args) {
...
Host host = new Host();
Data data1 = host.request(10, 'A');
Data data2 = host.request(20, 'B');
Data data3 = host.request(30, 'C');
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println("data1 = " + data1.getContent());
System.out.println("data2 = " + data2.getContent());
System.out.println("data3 = " + data3.getContent());
}
}
public class Host {
public Data request(final int count, final char c) {
final FutureData future = new FutureData(); // 引換券を作成
new Thread() {
public void run() {
RealData realdata = new RealData(count, c);
future.setRealData(realdata);
}
}.start();
return future; // 引換券を返す
}
}
public interface Data {
public abstract String getContent();
}
public class FutureData implements Data {
private RealData realdata = null;
private boolean ready = false;
public synchronized void setRealData(RealData realdata) {
if (ready) {
return; // balk
}
this.realdata = realdata;
this.ready = true;
notifyAll();
}
public synchronized String getContent() {
while (!ready) {
try {
wait();
} catch (InterruptedException e) {
}
}
return realdata.getContent();
}
}
Hostクラスは、まず最初にFutureDataのインスタンスを作る。このインスタンスが戻り値となる。次に新しいスレッドを起動し、その中でRealDataのインスタンスを作る。RealDataのインスタンスを作るには時間がかかる。インスタンスができたら、setRealDataメソッドを呼び出してそれをFutureDataインスタンスにセットする。
FureDataクラスは、引換券になるクラス。realDataフィールドは、これから作られるRealDataのインスタンスを保持するフィールド。このフィールドはsetRealDataメソッドによって代入される。getContentメソッドは、実際のデータを得るためのメソッド。setRealDataによってrealdataがセットされるのを待つ。セットされていればすぐに戻り、セットされていなければwaitする。waitで待っているスレッドは、setRealDataの中で呼び出しているnotifyAllで起こされる。
##登場人物
Client役
Client役は、Host役に対してリクエストを出し、結果(戻り値)としてすぐにVirtualData役を受け取る。ここで受け取るVirtualData役は、実際にはFuture役。Client役は、戻り値がRealData役なのかFuture役なのかを知る必要はない。サンプルプログラムでは、Mainクラスがこの役をつとめた。
Host役
Host役は、新しいスレッドを作りRealData役を作り始める。一方、Client役へがFuture役をVirtualData役として返す。サンプルプログラムでは、Hostクラスがこの役をつとめた。
VirtualData役
VirtualData役は、Future役とRealData役を同一視させる役。サンプルプログラムでは、Dataインターフェースがこの役をつとめた。
RealData役
RealData役は、実際のデータを表す役。このオブジェクトを作るには時間がかかる。サンプルプログラムでは、RealDataクラスがこの役をつとめた。
Future役
Future役は、RealData役の引換券として、Host役からClient役に渡される役。Future役は、Client役に対してはVirtualData役として振る舞う。Client役から操作された場合、RealData役が出来上がるまでは、スレッドはwaitで待つ。サンプルプログラムでは、FureDataクラスがこの役をつとめた。
##考えを広げるためのヒント
待たせないFuture役
getContentメソッドを非同期的に実装することも可能。getContentメソッドの中で新しいスレッドを作るというややこしい話ではなく、Balkingパターンを使って、「できていなかったらいったん帰る」ようにすればよい。
コールバックとFutureパターン
処理の完了を待って戻り値を得たいときコールバックという方法も考えられる。コールバックというのは、処理が完了したときにHost役が起動したスレッドが、Client役のメソッドを呼び返す方法である。
関連
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その1)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その2)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その3)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その4)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その5)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その6)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その7)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その8)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その9)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その10)