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

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

【プログラミング 43-44時間目】Guarded Suspentionパターン

<スポンサーリンク>

デザインパターンのマルチスレッド編です。
マルチスレッド処理は複雑にコードが複雑になりがちなので、設計の知恵袋であるデザインパターンを活用することがより大切になってきます。

今回はGuarded Suspentionパターンを使ってみました。
Guardedとは、守られるという意味です。
Suspentionとは、一時停止することという意味です。

例えば寿司屋で、「皿」と皿の上には「お寿司」という属性があるとします。
「皿から寿司をとって食べる」人がいて、その人の振る舞いは「寿司を食べる」
「皿に寿司を置く人」がいて、その人は「寿司を置く」とします。

寿司が置かれていないときに皿から寿司を取ろうとしたら、おかしいでしょう。
ひたすら寿司を取る振る舞いを、寿司が無いのに続けるのは滑稽でしょう。

Guarded Suspentionとは、そういうおかしな振る舞いを一時停止する処理を実装します。
この例だと、「寿司が皿に置かれるまでは寿司を取らない」ようにします。


さて、今回はデザインパターンとして、
バスケットのシューティングのシーンを想定しました。

登場人物は以下のとおりです。
・ボールカゴ
 ボールを詰め込みます。

・シューター
 100本シュートを打ちます。カゴにボールがあれば、その分シュートを打ちます。

・マネージャー
 シューターにボールを渡します。かごにボールがないときは、「無いよ!」と知らせてくれます。

・ボール
 その名の通り、ボールです。シューターの名前が書いてます。

・監督
 号令をかけます。要は始めの起点。Mainクラスです。サブキャラだけど。

まずはボールカゴから。

package my.guarded_suspension;

import java.util.LinkedList;
import java.util.Queue;

public class BallKago {
	private final Queue<Ball> queue = new LinkedList<Ball>();
	private int shootHonsu = 0;
	public synchronized Ball getBall() {
		while (queue.peek() == null) {
			try {
				System.out.println("かごにボールが入ってないよ!");
				wait();
			} catch (InterruptedException e) {
			}
		}
		return queue.remove();
	}
	public synchronized void putBall(Ball ball) {
		queue.offer(ball);
		shootHonsu++;
		System.out.println(shootHonsu + "本目のシュートです");
		notifyAll();
	}
}

シューター

package my.guarded_suspension;

import java.util.Random;

public class Shooter extends Thread {
	private final Random random;
	private final BallKago kago;
	public Shooter(BallKago kago, String name, int tairyoku) {
		super(name);
		this.kago = kago;
		this.random = new Random(tairyoku);
	}
	public void run() {
		//100本シュートを打つ
		for (int i = 1; i < 100; i++) {
			Ball ball = new Ball(Thread.currentThread().getName());
			System.out.println(ball.getShooterName() + " 君がシュートを打っています!" + ball);
			kago.putBall(ball);
			try {
				Thread.sleep(random.nextInt(3000));
			} catch(InterruptedException ignore) {
			}
			
		}
		System.out.println("シュート練終わり〜!!");
	}

}

マネージャー

package my.guarded_suspension;

import java.util.Random;

public class BijinManager extends Thread {
	private final Random random;
	private final BallKago kago;

	public BijinManager(BallKago kago, String managerName, int tairyoku) {
		super(managerName);
		this.kago = kago;
		this.random = new Random(tairyoku);
		System.out.println("私の名前は " +managerName + " だよ。よろしくね!");
	}
	
	public void run() {
		for (int i = 0; i < 100; i++) {
			Ball ball = kago.getBall();
			System.out.println("私は" + Thread.currentThread().getName() + "。かごにボールが入ってたら君に渡すから" + ball.getShooterName() + "君を応援してるから。。");
			try {
				Thread.sleep(random.nextInt(3000));
			} catch(InterruptedException ignore) {
				
			}
		}
	}
	
}

ボール

package my.guarded_suspension;

public class Ball {
	private final String shooterName;
	public Ball(String shooterName) {
		this.shooterName = shooterName;
	}
	public String getShooterName() {
		return shooterName;
	}
	public String toString() {
		return "[ シューターの名前は " + shooterName + " ]";
	}
}

監督

package my.guarded_suspension;

public class Kantoku {
	public static void main(String[] args) {
		BallKago kago = new BallKago();
		System.out.println("シューティング開始ーっ!!");
		new BijinManager(kago, "まなみ", 3000).start();
		new Shooter(kago, "かずや", 5000).start();
	}
}

結果はこのようになります。

シューティング開始ーっ!!
私の名前は まなみ だよ。よろしくね!
かごにボールが入ってないよ!
かずや 君がシュートを打っています![ シューターの名前は かずや ]
1本目のシュートです
私はまなみ。かごにボールが入ってたら君に渡すからかずや君を応援してるから。。
かずや 君がシュートを打っています![ シューターの名前は かずや ]
2本目のシュートです
私はまなみ。かごにボールが入ってたら君に渡すからかずや君を応援してるから。。
かずや 君がシュートを打っています![ シューターの名前は かずや ]
3本目のシュートです
・・・
かずや 君がシュートを打っています![ シューターの名前は かずや ]
99本目のシュートです
私はまなみ。かごにボールが入ってたら君に渡すからかずや君を応援してるから。。
シュート練終わり〜!!

こんな感じで、ボールがカゴに入っていない時はシュートを打たないようにすることができました。