読者です 読者をやめる 読者になる 読者になる

感謝のプログラミング 10000時間

たどり着いた結果(さき)は、感謝でした。

Thread-Per-Messageパターン(マルチスレッドデザインパターン)

<スポンサーリンク>

Thread-Per-Messageパターンは、別のクラスに仕事を投げて、自分は次の仕事をさっさとやってしまうパターンである。
perというのは「〜毎の」という意味で、thread per messageとはメッセージ毎のスレッドという意味になる。

このクラス図でいうと、ClientがHostに仕事を投げると、Hostその仕事ごとにスレッドを起動します。
そこで働くのはHelperクラスです。Helperのhandleがスレッドごとに仕事をして、要求を処理します。
Host役が作る新しいスレッドがHelperを利用するようになっています。

Thread-Per-Messageパターンの目的は、応答性を上げ、遅延時間を下げることです。
Clientに対するHostの応答性が上がるため、遅延時間が下がります。

Helperがやっているhandleの処理に時間がかかる場合は、1つの仕事をさばいているスレッドが走っている間に、HostからClientに応答することができます。
このパターンを使わないと、一つ一つの仕事が終わるまでClientは次の仕事ができなくなってしまいます。

Thread-Per-Messageパターンは、処理の順序を気にしないときに使います。
また、handleの実行結果はrequestする側で受け取ることはできないので、処理の結果を判断する時には使えません。

これは、メソッド呼び出しとスレッド起動を利用して「非同期のメッセージ送信」を実現しているという捉え方ができます。

それでは、サンプルを見てます。
部長を想像してください。

部長(Client)は仕事を作り、プロジェクトマネージャー(Host)に振ります。

そのプロジェクトマネージャーは自分で手を動かすことはありません。
仕事を色んな部下(Helper)に振ります。

部長(Client)はPMに仕事を振れば、部長の役割は終わりです。
進捗レビューもしない、丸投げ部長、丸投げPMを想定してください。

その例がこちら。
まずは、部長(Client)から仕事が降られます。

package thread_per_message2;

public class Boss {
	public static void main(String[] args) {
		System.out.println("おい、仕事やっといて");
		Host marunage = new Host();
		marunage.request("タスクNo.1","コピー取り");
		marunage.request("タスクNo.2","議事録");
		marunage.request("タスクNo.3","掃除");
		
		System.out.println("仕事は頼み終わったから俺は帰るぞ");
	}
}

仕事を振られたPM(Host)は、タスクごとに部下(Helper)スレッドを走らせます。

package thread_per_message2;

public class Host {
	private final Helper buka = new Helper();
	public void request(final String taskNo, final String taskName) {
		System.out.println("--------------------------");
		System.out.println("  仕事を受け取ったので部下に投げます。" + taskNo + "の「" + taskName + "」を始めます");
		new Thread() {
			public void run() {
				buka.handle(taskNo, taskName);
			}
		}.start();
		System.out.println("");
		System.out.println("さて、おれも部下に仕事を投げたぜ。投げた仕事は・・・" + taskNo + "で、「" + taskName + "」か。投げ終わったら帰ろう");
		System.out.println("------------------------------");
	}
}

実際に手を動かすのは部下(Helper)です。handle()メソッドで仕事を処理しています。

package thread_per_message2;

public class Helper {
	public void handle(String taskNo, String taskName) {
		System.out.println("----------------------");
		System.out.println("俺の名前は・・・" + Thread.currentThread().getName());
		System.out.println("うわ〜あいつ丸投げしやがった・・・。やらなきゃいかん仕事は" + taskNo + "の「" + taskName + "」か・・・");
		for(int i = 0; i < 6; i++) {
			slowly();
			System.out.println("俺の名は" + Thread.currentThread().getName() + "だ。" + taskName + "をやっています・・・");
		}
		System.out.println("");
		System.out.println(taskName + "が終わりました...");
		System.out.println("-------------------------------");
	}
	private void slowly() {
		try {
			Thread.sleep(1000);
		} catch (InterruptedException ignore) {
			
		}
	}
}

で、これを実行した結果は以下の通り。
部長(Client)は仕事を投げたら帰っているし、PM(Host)も部下(Helper)を動かしたら帰っています。
部下(Helper)は全部の仕事が終わるまで仕事をします。

おい、仕事やっといて
--------------------------
  仕事を受け取ったので部下に投げます。タスクNo.1の「コピー取り」を始めます

さて、おれも部下に仕事を投げたぜ。投げた仕事は・・・タスクNo.1で、「コピー取り」か。投げ終わったら帰ろう
------------------------------
--------------------------
  仕事を受け取ったので部下に投げます。タスクNo.2の「議事録」を始めます
----------------------
俺の名前は・・・Thread-0
うわ〜あいつ丸投げしやがった・・・。やらなきゃいかん仕事はタスクNo.1の「コピー取り」か・・・

さて、おれも部下に仕事を投げたぜ。投げた仕事は・・・タスクNo.2で、「議事録」か。投げ終わったら帰ろう
------------------------------
--------------------------
  仕事を受け取ったので部下に投げます。タスクNo.3の「掃除」を始めます
----------------------
俺の名前は・・・Thread-1
うわ〜あいつ丸投げしやがった・・・。やらなきゃいかん仕事はタスクNo.2の「議事録」か・・・

さて、おれも部下に仕事を投げたぜ。投げた仕事は・・・タスクNo.3で、「掃除」か。投げ終わったら帰ろう
------------------------------
仕事は頼み終わったから俺は帰るぞ
----------------------
俺の名前は・・・Thread-2
うわ〜あいつ丸投げしやがった・・・。やらなきゃいかん仕事はタスクNo.3の「掃除」か・・・
俺の名はThread-0だ。コピー取りをやっています・・・
俺の名はThread-2だ。掃除をやっています・・・
俺の名はThread-1だ。議事録をやっています・・・
俺の名はThread-0だ。コピー取りをやっています・・・
俺の名はThread-1だ。議事録をやっています・・・
俺の名はThread-2だ。掃除をやっています・・・
俺の名はThread-0だ。コピー取りをやっています・・・
俺の名はThread-2だ。掃除をやっています・・・
俺の名はThread-1だ。議事録をやっています・・・
俺の名はThread-0だ。コピー取りをやっています・・・
俺の名はThread-1だ。議事録をやっています・・・
俺の名はThread-2だ。掃除をやっています・・・
俺の名はThread-0だ。コピー取りをやっています・・・
俺の名はThread-2だ。掃除をやっています・・・
俺の名はThread-1だ。議事録をやっています・・・
俺の名はThread-0だ。コピー取りをやっています・・・

コピー取りが終わりました...
-------------------------------
俺の名はThread-2だ。掃除をやっています・・・

掃除が終わりました...
-------------------------------
俺の名はThread-1だ。議事録をやっています・・・

議事録が終わりました...
-------------------------------

これがThread-Per-Messageパターンです。<参考にしたテキスト>

増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編

増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編