Javaマルチスレッド--生産者と消費者の問題


説明
Javaでは、スレッド間の通信は主にjavaである.lang.Objectクラスが提供するwait、notify、notifyAllの3つの方法を完了します.
1オブジェクトのwaitメソッドが呼び出されると、スレッドはオブジェクトの待機キューに入り、オブジェクトロックを解放し、他のスレッドはこのオブジェクトロックを競合して使用することができる.sleep法はスレッドをスリープ状態にしたが,スレッドが占有するリソースは解放されなかった.
2オブジェクトのnotifyメソッドが呼び出されると、このメソッドはオブジェクトの待機キューからランダムにスレッドを取り出して起動する.notifyAllは、待機キュー内のすべてのスレッドを起動し、他の実行中のスレッドと競合するオブジェクトロックです.
③wait、notify、notifyAllの3つの方法はsynchronizedの作用の範囲内にしか現れない.複数のスレッドが同じオブジェクトを待機し、待機条件が異なる場合はnotifyAllメソッドを使用します.notifyAllは、不要なスレッドも起動するため、パフォーマンスが低下します.一方、notifyが起動したスレッドは、所望のスレッドではない可能性があります.
生産者と消費者の問題
ここで,ある種のリソース(クラスGoodsで表される)を用いたスレッドを消費者Consumer,同種のリソースを生成するスレッドをProducerと呼ぶ.ConsumerとProducerが連携できるようにするには、次のルールに従います.
(1)バッファbufferにスペースがあれば、生産者Producerはその中に資源を保存することができる.バッファbufferがいっぱいになった場合、生産者Producerを待機させ、自分が取得したオブジェクトロックを放棄し、待機キューに入る.
(2)バッファにリソースが利用可能であれば、消費者Consumerはbufferからリソースを取り出すことができる.bufferが空の場合、Consumerを待機させ、自分が取得したオブジェクトロックを放棄し、待機キューに入る.
(3)ProducerとConsumerはbufferを同時に読み書きできない.したがってbufferを操作する方法increase(生産資源)とdecrease(消費資源)はsynchronizedを使用して同期する必要がある.
/**
 * Goods ,      
 */
package com.hh.Producer.Consumer;

public class Goods {
	private final int SIZE = 5;
	private int buffer = 0;

	/**
	 *       
	 */
	public synchronized int increase() {
		if (buffer < SIZE) {
			++buffer;
			notify(); //          
		} else {
			try {
				wait(); //        ,         notify  
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		return buffer;
	}

	/**
	 *       
	 */
	public synchronized int decrease() {
		if (buffer > 0) {
			--buffer;
			notify(); //          
		} else {
			try {
				wait(); //        ,         notify  
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		return buffer;
	}

	public int getGoodsSize() {
		return SIZE;
	}

}
/**
 * Producer ,       
 */
package com.hh.Producer.Consumer;

public class Producer implements Runnable {

	private Goods goods;

	public Producer(Goods goods) {
		this.goods = goods;
	}

	@Override
	public void run() {
		while (true) {
			int goodsCount = goods.increase();
			if (goodsCount != goods.getGoodsSize()) {
				System.out.println("          ,     :" + goodsCount);
			} else {
				System.out.println("    ,     ");
			}
			try {
				Thread.sleep((int) (Math.random() * 1000));
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
/**
 * Consumer ,     
 */
package com.hh.Producer.Consumer;

public class Consumer implements Runnable {

	private Goods goods;

	public Consumer(Goods goods) {
		this.goods = goods;
	}

	@Override
	public void run() {
		while (true) {
			int goodsCount = goods.decrease();
			if (goodsCount != 0) {
				System.out.println("          ,     :" + goodsCount);
			} else {
				System.out.println("    ,     ");
			}
			try {
				Thread.sleep((int) (Math.random() * 1000));
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

}
/**
 *      Producer    Consumer       
 */
package com.hh.Producer.Consumer;

public class TestMain {
	
	public static void main(String[] args) {
		Goods goods = new Goods();
		Producer producer = new Producer(goods);
		Consumer consumer = new Consumer(goods);

		new Thread(producer).start();
		new Thread(consumer).start();
	}

}

注意:共有リソースGoodsクラスでwaitとnotify操作を行うのは、ProducerまたはConsumerクラスではありません.
このような問題に対しては,スレッド間の通信問題をよりよく理解するために,必ず自分でサンプルコードを書く必要がある.