生産者消費者[簡単な例]

3892 ワード

class Meal{
	private final int orderNum;
	Meal(int orderNum){
		this.orderNum = orderNum;
	}
	public String toString(){
		return "Meal " + orderNum;
	}
}

class WaitPerson implements Runnable{
	private Restaurant restaurant;
	public WaitPerson(Restaurant r){
		restaurant = r;
	}
	public void run(){
		try {
			while (!Thread.interrupted()) {
				synchronized (this) {
					while (restaurant.meal == null)
						wait();
				}
				System.out.println("Waitperson got " + restaurant.meal);
				synchronized (restaurant.chef) {
					restaurant.meal = null;
					restaurant.chef.notifyAll(); // ready for another
				}
			}
		} catch (Exception e) {
			System.out.println("WaitPerson interrupted");
		}
	}
}
class Chef implements Runnable{
	private Restaurant restaurant;
	private int count = 0;
	public Chef(Restaurant r){
		restaurant = r;
	}
	public void run(){
		try {
			while(!Thread.interrupted()){
				synchronized(this){
					while(restaurant.meal!=null)
						wait(); 	// for the meal to be taken;
				}
				if(++count == 10){
					System.out.println("Out of fodd, clsosing");
					restaurant.exec.shutdownNow();
				}
				System.out.println("Order up! ");
				synchronized(restaurant.waitPerson){
					restaurant.meal = new Meal(count);
					restaurant.waitPerson.notifyAll();
				}
				TimeUnit.MILLISECONDS.sleep(100);
			}
		} catch (InterruptedException e) {
			System.out.println("Chef interrupted");
		}
	}
}

public class Restaurant {
	Meal meal;
	ExecutorService exec = Executors.newCachedThreadPool();
	WaitPerson waitPerson = new WaitPerson(this);
	Chef chef = new Chef(this);
	public Restaurant(){
		exec.execute(chef);
		exec.execute(waitPerson);
	}
	public static void main(String[] args) {
		new Restaurant();
	}
}

 
 
RestaurantはWaitPersonとChefの焦点で、彼らはどのRestaurantのために働いているのかに戻らなければなりません.
彼らはこのホテルの「食事窓」と付き合わなければならないので、restaurantを置いたり取ったりしなければならない.meal. run()
で、WiatPersonはwait()モードに入り、ChefのnotifyAll()に呼び覚まされるまでタスクを停止します.
 
これは非常に簡単なプログラムなので、WaitPersonのロックに1つのタスクしかありません.
WaitPersonタスク自体が待機します.このため、理論的にはnotify()ではなくnotify()を呼び出すことができる.
notifyAll(). ただし、より複雑な場合、特定のオブジェクトに複数のタスクがある場合があります.
待機しているので、どのタスクが起動されるべきか分からないので、notifyAll()を呼び出すほうが安全です.
これにより、このロックを待つすべてのタスクを呼び覚ますことができ、各タスクはこの通知が
自分に関わる.
 
wait()はwhile()文に包装されており、この文は待っている食べ物を絶えずテストしていることに注意してください.
ちょっと変に見えますが、注文を待っていると、目が覚めると、この注文は入手可能でなければなりませんよね?
前述したように、問題は同時アプリケーションで、ある商談のタスクがWaitPersonで呼び覚まされる可能性があります.
突然足を踏み入れて注文を取るのですが、唯一安全な方法は次のwait()の慣用法を使うことです.
while(conditionIsNotMet)
wait();
 
NotifyAll()の呼び出しはwaitPersonのロックを最初にキャプチャしなければならないが、WatiPersonでは.run()中
のwati()の呼び出しは自動的にこのロックを解放するので、これは実現可能である.呼び出すから
notifyAll()は必ずこのロックを持つので、2つのオブジェクトが統一オブジェクト上でnotifyAll()を呼び出そうとすることを保証します.
のタスクは互いに衝突しません.
 
run()メソッド全体を1つのtry文ブロックに配置することにより、この2つのrun()メソッドを設計することができる.
秩序正しく閉じることができます.catchはrun()メソッドの終了カッコのすぐ前に終了するので、このタスクが
InterruptedException例外が受信され、例外が取得された直後に終了します.
 
なお、ChefではshutdownNow()が呼び出された後、run()から直接戻るべきであり、通常はこれが
君はやるべきだ.しかし、このような方法で実行するにはもっと面白いものがあります.shutdownNow()はすべてに
ExecutorServiceによって開始されたタスクはinterrupt()を送信しますが、Chefではタスクは取得されません.
interrupt()は、タスクが(中断可能な)ブロック操作に入ろうとすると、この
割り込みはInterruptedExceptionのみ放出できます.そこで、まず「Order up!」と表示され、そして
ChefがSlee()を呼び出そうとするとInterruptedExceptionが投げ出される.slee()の呼び出しを削除すると、
このタスクはrun()ループの上部に戻り、Thread.interrupted()テストで終了し、
同時に異常を投げ出さない.