// ProducerConsumerTest.java public class ProducerConsumerTest { public static void main(String[] args) { CubbyHole c = new CubbyHole(); Producer p1 = new Producer(c, 1); Consumer c1 = new Consumer(c, 1); p1.start(); c1.start(); } }ProducerクラスはThreadのサブクラスで, そのrunメソッドは,オブジェクト内部に 保存しているCubbyHoleオブジェクトに対して 10個の数値を乱数で生成される待ち時間ごとに ひとつづつputする.
// Producer.java public class Producer extends Thread { private CubbyHole cubbyhole; private int number; public Producer(CubbyHole c, int number) { cubbyhole = c; this.number = number; } public void run() { for (int i = 0; i < 10; i++) { cubbyhole.put(i); System.out.println("Producer #" + this.number + " put: " + i); try { sleep((int)(Math.random() * 100)); } catch (InterruptedException e) { } } } }Consumerクラスは,Producerにも保存されて いるCubbyHoleオブジェクトから10個の数値を 待ち時間無く順にgetする.
// Consumer.java public class Consumer extends Thread { private CubbyHole cubbyhole; private int number; public Consumer(CubbyHole c, int number) { cubbyhole = c; this.number = number; } public void run() { int value = 0; for (int i = 0; i < 10; i++) { value = cubbyhole.get(); System.out.println("Consumer #" + this.number + " got: " + value); } } }CubbyHoleクラスは,以下のようになる. CubbyHoleのcontents,availableデータのように,privateがついているデータは そのクラスからのみアクセス可能なものとなる.
// CubbyHole.java public class CubbyHole { private int contents; private boolean available = false; public synchronized int get() { while (available == false) { try { // wait for Producer to put value wait(); } catch (InterruptedException e) { } } available = false; // notify Producer that value has been // retrieved notifyAll(); return contents; } public synchronized void put(int value) { while (available == true) { try { // wait for Consumer to get value wait(); } catch (InterruptedException e) { } } contents = value; available = true; // notify Consumer that value has been set notifyAll(); } }もし,getとputメソッドをwhile文でなく 次のようにすると,もし,availableがfalseで Producerが何もputしていないという状態が起こった場合, getメソッドは何も返せなくなる. また,Consumerが値をgetする前にProducerが putを呼んでしまうと,synchronizedされている getメソッドは何もできなくなってしまう. そのため,waitメソッドとnotifyまたはnotifyAllメソッドを 使う.
public synchronized int get() { // won't work! if (available == true) { available = false; return contents; } } public synchronized void put(int value) { // won't work! if (available == false) { available = true; contents = value; } }実行は以下のようになる.
% java ProducerConsumerTest Producer #1 put: 0 Consumer #1 got: 0 Consumer #1 got: 1 Producer #1 put: 1 Producer #1 put: 2 Consumer #1 got: 2 Consumer #1 got: 3 Producer #1 put: 3 Consumer #1 got: 4 Producer #1 put: 4 Producer #1 put: 5 Consumer #1 got: 5 Consumer #1 got: 6 Producer #1 put: 6 Consumer #1 got: 7 Producer #1 put: 7 Producer #1 put: 8 Consumer #1 got: 8 Consumer #1 got: 9 Producer #1 put: 9ここでは,getとputのメソッドにsynchronizedキーワードが ついている. synchronizedメソッドがあるオブジェクトに対して実行されると そのメソッドが修了するまで,そのオブジェクトに適用可能な メソッドのうち,synchronizedメソッドは同時には実行できなく なる.今の場合,あるCubbyHoleオブジェクト(cubbyhole)に 対してputメソッドが実行中には,他のスレッドがそのオブジェクトに 大してputメソッドやgetメソッドを実行できなくなる. synchronizedがついていないメソッドは実行が可能. ここで,getとputがputされた後に確実にgetを行うという 同期を取るために, クラスObjectで定義されている waitメソッドとnotifyAllメソッドを使っている.