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

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

Worker Threadパターンについて

スポンサーリンク

会社に尊敬する先輩がいる。
その人は僕の6歳年上だ。ゼロから勉強したはずなのに、iOSアプリも一ヶ月くらいで作りあげてしまった。
経験値が高いと、新しいことの習得も早い。

その人のさらに上にはもっともっと優秀な人がいる。

6歳年上のプログラマーが6合目にいるとしたら、僕はまだ0.5合目だ。
もっともっと上にたくさんの人がいる。大きな価値を生み出している。

そして、まだ会ったことのないところに、もっともっとすごい人がたくさんいるんだろう。

果てなく遠い、偉大なプログラマーへの道。

今の夜中までやってるプログラミングの勉強が報われるかはわからない。
報われないかもしれない。
たとえ10000時間プログラミングの勉強をしたところで、誰にも追いつけないかもしれない。

でも、こればっかりはやってみないとわからない。
才能があるか、ないかなんて、本当に頑張ってみないとわからないんだ。
だからやる。業務以外で、10000時間。業務は実践の場だからまた別。
新しいことを学び続ける10000時間を積み重ねる。
あと9916時間プログラムを書き続けて、それでも下手くそだったらその時は自分に才能が無かったんだと諦めよう。でもそれまでは、才能の問題ではない。努力の問題だと。

この山を登ると決めたから。

10000時間をやり遂げた後は、ナメック星に着く直前に、100倍重力の修行を終えた悟空みたいに

「軽い・・体が軽い・・・!」

みたいに別人のようになっていることを期待したい。

★★
・Worker ThreadパターンやThread-Per-Messageパターンでは、意識的にメソッドの起動とメソッドの実行を分離する。

・メソッドを起動することをinvocationといい、メソッドを実行することをexecutionという。

・Worker ThreadやThread-Per-Messageはinvocationとexecuteを分離している。

■起動と実行を分離することの意義
・起動が実行に引きずられないため、応答性の向上が見込める。
・実行順序を制御できる。
・「起動したけど、実行しない」というような、キャンセル機能を実現できる。
・invokeするコンピュータとexecuteするコンピュータを分けることにより、分散処理が実現できる。

[想像しよう]
Worker Threadパターンを現実にたとえてみる。
外でガンガン仕事を取ってくる上司がいるとする。

その上司が営業して取ってきた仕事を部下がこなしていく。
しかし、その会社に「上司」と「部下」の二人しかいなかったら、上司は部下の仕事が終わるまで、新しい仕事を取ってくることができない。外に営業をかけにいくことができないのだ。

そこで表れるのが、仲介役の「丸投げ君」である。
自分で仕事をするわけではないけれど、「上司」が取ってきた仕事を仲介役が受け取って、それを部下に流す。

「仲介役に仕事を渡すまでが上司の仕事」と考えることができれば、どんないいことがあるのか。
上司は部下の仕事の結果報告を待たずに仕事をどんどん受け取ってくることができるのだ。
受けた仕事は仲介役に投げる。
仲介役が仕事を受け取るたびに、その仕事を部下が消化していく。

このように、仕事の起動(invocation)と実行(execution)を別にすることで、たとえ仕事の実行に
時間がかかっても、どんどん新しい仕事を起動できるようになる。

片方のスレッドが片方のスレッドに及ぼす影響を小さくすることができるのである。

[サンプル]
上司の役(デザインパターン的にはClient)
中を見ると仕事を作って、仲介役(dentatsu)に渡している。

package worker_thread2;

import java.util.Random;

public class JhoshiThread extends Thread {
	private final Dentatsu dentatsu;
	private static final Random random = new Random();
	
	public JhoshiThread(String name, Dentatsu dentatsu) {
		super(name);
		this.dentatsu = dentatsu;
	}
	public void run() {
		try {
			for (int i = 0; true ; i++) {
				Shigoto shigoto = new Shigoto(getName(), i);
				dentatsu.putShigoto(shigoto);
				Thread.sleep(random.nextInt(1000));
			}
		} catch (InterruptedException ignore){
			
		}
	}

}

で、上司が投げる「仕事」を表現するクラス(デザインパターン的にはRequest)
仕事の「振る舞い」は「実行する」というものがある。
実行といっても、今回は標準出力にメッセージを表示するだけだが・・・。

package worker_thread2;

import java.util.Random;

public class Shigoto {
	private final String iraisha;  //依頼者
	private final int number; //仕事の項番
	private static final Random random = new Random();
	public Shigoto(String name, int number) {
		this.iraisha = name;
		this.number = number;
	}
	//仕事の実行を表現する
	public void execute() {
		System.out.println(Thread.currentThread().getName() + " が仕事実行しています。" + this);
		try {
			//仕事はたまり続ける・・・
			Thread.sleep(random.nextInt(1000));
		} catch(InterruptedException ignore) {
			
		}
	}
	public String toString() {
		return "[ 仕事の依頼者は・・・" + iraisha + " 様です。仕事の番号は・・・" + number + "です]";
	}
	
}

それで、実は肝となる伝達役。デザインパターン的にはChannelという。
Queueで仕事の仲介を表現している。
上司がQueueに仕事を詰め、部下が仕事をとっていく。
それを仲介するQueueがこのChannelである。

package worker_thread2;

public class Dentatsu {
	//仲介できる仕事の限界量
	private static final int GENKAI_MARUNAGE = 100;
	private final Shigoto[] shigotoQueue;
	private int tail; //次に仕事を入れられる場所
	private int head; //次に仕事を持っていく場所
	private int count; //仕事の数
	
	private final BukaThread[] threadPool;
	
	public Dentatsu(int threads) {
		//仕事の限界量を配列で表現する
		this.shigotoQueue = new Shigoto[GENKAI_MARUNAGE];
		this.head = 0;
		this.tail = 0;
		this.count = 0;
		
		threadPool = new BukaThread[threads];
		for (int i = 0; i < threadPool.length; i++) {
			threadPool[i] = new BukaThread("部下 " + i + "号" , this);
		}
	}
	
	public void startBukas() {
		for (int i = 0; i < threadPool.length; i++) {
			threadPool[i].start();
		}
	}
	//仕事を詰めるメソッド
	public synchronized void putShigoto(Shigoto shigoto) {
		//限界数まで仕事はキューに詰め込まれる(ブラック企業)
		//限界を超えたら、余裕ができるまで待つ(waitする)
		while (count >= shigotoQueue.length) {
			try {
				wait();
			} catch(InterruptedException ignore) {
				
			}
		}
		shigotoQueue[tail] = shigoto;
		tail = (tail + 1) % shigotoQueue.length;
		count++;
		notifyAll();
	}
	
	//仕事を持っていくメソッド
	public synchronized Shigoto takeShigoto() {
		//仕事がなかったらずっと待ち続ける
		while(count <= 0) {
			try {
				wait();
			} catch(InterruptedException ignore) {
				
			}
		}
		Shigoto shigoto = shigotoQueue[head];
		head = (head + 1) % shigotoQueue.length;
		count--;
		notifyAll();
		return shigoto;
	}
	
}

で、健気に仕事を撮り続ける部下のクラス。
デザインパターン的にはWorkerクラスである。

package worker_thread2;

public class BukaThread extends Thread {
	private final Dentatsu dentatsu;
	public BukaThread(String name, Dentatsu dentatsu) {
		super(name);
		this.dentatsu = dentatsu;
	}
	public void run() {
		while (true) {
			//仕事があったら取る
			Shigoto shigoto = dentatsu.takeShigoto();
			//仕事を実行する
			shigoto.execute();
		}
	}

}

それでは、適当にMainクラスを作って実行してみる。

package worker_thread2;

public class Main {
	public static void main(String[] args) {
		//4人の伝達係
		Dentatsu dentatsu = new Dentatsu(4);
		dentatsu.startBukas();
		//3人の上司
		new JhoshiThread("蒲地敏幸",dentatsu).start();
		new JhoshiThread("桑野哲司",dentatsu).start();
		new JhoshiThread("重松平八郎",dentatsu).start();
	}

}

結果は・・・

部下 0号 が仕事実行しています。[ 仕事の依頼者は・・・重松平八郎 様です。仕事の番号は・・・4です]
部下 2号 が仕事実行しています。[ 仕事の依頼者は・・・蒲地敏幸 様です。仕事の番号は・・・2です]
部下 3号 が仕事実行しています。[ 仕事の依頼者は・・・桑野哲司 様です。仕事の番号は・・・2です]
部下 1号 が仕事実行しています。[ 仕事の依頼者は・・・重松平八郎 様です。仕事の番号は・・・5です]
部下 1号 が仕事実行しています。[ 仕事の依頼者は・・・桑野哲司 様です。仕事の番号は・・・3です]
部下 0号 が仕事実行しています。[ 仕事の依頼者は・・・桑野哲司 様です。仕事の番号は・・・4です]
部下 2号 が仕事実行しています。[ 仕事の依頼者は・・・蒲地敏幸 様です。仕事の番号は・・・3です]

こんな感じで、上司が仕事をとってきて、部下が実行するパターンを表現することができた。

これをWorker Threadパターンという。<超参考書籍>

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

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

結城先生にはいつも本当にお世話になっている。
著作はほぼ全て買っているが、デザインパターンは特にわかりやすい。