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

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

wait()とnotifyAll()を使ってみる。

スポンサーリンク

wait(),notify(),notifyAll()メソッドはObjectクラスに実装されています。
Objectクラスに実装されているわけではありません。

それぞれのメソッドの意味は以下のとおりです。

メソッド 意味
wait() 通知があるまでスレッドを待たせる
notify() 待ち状態の1つのスレッドに解除の通知を行う
notifyAll() 待ち状態のすべてのスレッドに解除の通知を行う

wait,notify,notifyAllメソッドは必ず、synchronizedメソッドまたは、synchronizedブロック内で利用する必要があります。
これらのメソッドを使いこなすことが、Javaのマルチスレッドを理解していく肝のように思います。

では、ここで銀行口座にひたすらお金を振り込む貢ぐ君と、銀行口座からひたすらお金を下ろす悪女を例に、wait()とnotifyAll()を見てみましょう。
銀行口座に金が入っていない時は、悪女はwait()しています。
貢ぐ君は入金したら、健気に悪女に知らせます。

結果から見ると、こんな風に表示されます。

707円引き出したいけど貯金が足りないわ!
お金が入金されるまで待つわ...
----------------------------
272円、貯金しまぁす!!
残りのお金は 272円になりましたぁ!
起きてください!!
----------------------------
226円引き出します。
残りのお金は 46円
----------------------------
816円引き出したいけど貯金が足りないわ!
お金が入金されるまで待つわ...
----------------------------
913円、貯金しまぁす!!
残りのお金は 959円になりましたぁ!
起きてください!!
----------------------------
364円引き出します。
残りのお金は 595円
----------------------------


以下がサンプルソースです。

・BankAccount.java
銀行口座。お金をおろしたり、預けたりできる。
引き出したい額より、貯金額が少ない場合はwait()する。
また、お金が預けられた時は、待っている人(waitしている人)をnotifyAll()で起こす。

package mitsugu;

public class BankAccount {

	/* お金の合計 */
	private int amount;


	public int getAmount() {
		return amount;
	}

	public void setAmount(int amount) {
		this.amount = amount;
	}

	public synchronized void deposit(int money) {
		System.out.println(money + "円、貯金しまぁす!!");
		amount = amount + money;
		System.out.println("残りのお金は " + amount + "円になりましたぁ!");
		System.out.println("起きてください!!");
		System.out.println("----------------------------");
		notifyAll();
	}

	public synchronized void withdraw(int money) {
		if (getAmount() < money) {
			try {
				System.out.println(money + "円引き出したいけど貯金が足りないわ!");
				System.out.println("お金が入金されるまで待つわ...");
				System.out.println("----------------------------");
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		} else {
			System.out.println(money + "円引き出します。");
			amount = amount - money;
			System.out.println("残りのお金は " + amount + "円");
			System.out.println("----------------------------");
		}
	}

}

・Request.java
ATMの操作をするイメージのインターフェース

package mitsugu;

public interface Request {
	public void execute();
}

・DepositRequest.java
一定のお金を振り込む処理のイメージ。

package mitsugu;

public class DepositRequest implements Request {
	private int deposit;
	private BankAccount account;


	public int getDeposit() {
		return deposit;
	}

	public void setDeposit(int deposit) {
		this.deposit = deposit;
	}

	public DepositRequest(BankAccount account) {
		this.account = account;
	}

	public void execute() {
		account.deposit(deposit);
	}

}

・WithDrawRequest.java
一定のお金を取り出す処理のイメージ

package mitsugu;

public class WithDrawRequest implements Request{
	private BankAccount account;
	private int withdraw;


	public BankAccount getAccount() {
		return account;
	}

	public void setAccount(BankAccount account) {
		this.account = account;
	}

	public int getWithdraw() {
		return withdraw;
	}

	public void setWithdraw(int withdraw) {
		this.withdraw = withdraw;
	}

	public WithDrawRequest(BankAccount account) {
		this.account = account;
	}

	public void execute() {
		account.withdraw(withdraw);
	}
}

・Akujyo.java

package mitsugu;

import java.util.Random;

public class Akujyo implements Runnable{
	private BankAccount account;

	private int withdraw;


	public int getWithdraw() {
		return withdraw;
	}


	public void setWithdraw(int withdraw) {
		this.withdraw = withdraw;
	}


	public Akujyo(BankAccount account) {
		this.account = account;
	}


	public void run() {
		for (int i=0; i < 100; i++) {
			Random random = new Random();
			WithDrawRequest request = new WithDrawRequest(account);
			request.setWithdraw(random.nextInt(1000));
			request.execute();

			try {
				Thread.sleep(random.nextInt(2000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

	}
}


・Mitsugu.java

package mitsugu;

import java.util.Random;

public class Mitsugu implements Runnable {
	private int deposit;
	private BankAccount account;

	public Mitsugu(BankAccount account) {
		this.account = account;
	}

	public void run() {
		while (true) {
			Random random = new Random();
			int deposit = random.nextInt(1200);
			DepositRequest request = new DepositRequest(account);
			request.setDeposit(deposit);
			request.execute();
			//notifyAll();

			try {
				Thread.sleep(2500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

	}
}

・Main.java

package action;

import mitsugu.Akujyo;
import mitsugu.BankAccount;
import mitsugu.Mitsugu;


public class Main {
	public static void main(String[] args) throws InterruptedException {
		BankAccount account = new BankAccount();

		Mitsugu m = new Mitsugu(account);

		Akujyo a = new Akujyo(account);

		Thread mitsugu = new Thread(m);
		Thread akujyo = new Thread(a);

		mitsugu.start();
		akujyo.start();

	}
}


<参考文献>

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

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