next up previous
Next: 14 待ちと通知のメソッド Up: ソフトウェア第三 講義資料 Java入門,宣言,インタフェース,パッケージ,スレッド Previous: 12.5 スタティックな同期メソッド

13 生産-消費(Producer-Consumer)プログラム

次に,CubbyHoleクラスのデータを生成するProducerクラス と,それを消費するConsumerクラスの例で,それぞれが 独立のスレッドで走るというProducer-Consumerプログラムを考える.
// 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メソッドを使っている.

generated through LaTeX2HTML. M.Inaba 平成18年5月7日