Previous | Next | Trail Map | Writing Java Programs | スレッドの制御


モニタ

Java 言語と実行時システムは、 C. A. R. Hoare の記事 「Communicating Sequential Processes 」(Communications of the ACM 、第21巻、No.8、1978年8月、666-677ページ)で最初に概説されたモニタの使用法によって、スレッドの同期をサポートする。 一般に、モニタは特定のデータ項目(条件変数)と関連づけられて、そのデータではロックとして機能する。 スレッドがいくつかのデータ項目のためにモニタを確保する時、他のスレッドはロックされて、データを検査あるいは変更することはできない。

個々の並行スレッドが同じデータ項目にアクセス可能なプログラムのコードセグメントは、危険領域として知られている。 Java 言語では、synchronized キーワードでプログラム内の危険領域を明らかにする。
注釈: 一般に、Java プログラムの危険領域はメソッドである。 synchronized としてより小さいコードセグメントをマークすることができるのだが、これがオブジェクト指向パラダイムに違反して、デバッグや維持が困難な紛らわしいコードにつながる。 Java プログラミング用途の大多数については、メソッドレベルにおいてのみ synchronized を使用するのが最良である。

Java 言語では、ただ1つのモニタが同期メソッドを持つすべてのオブジェクトと関連づけられる。 以前のページ で紹介したプロデューサ/コンシューマの例の CubbyHole クラスは、2つの同期メソッドを持っている。CubbyHole で値を変更するために使用する put()メソッドと、現在の値を検索するために使用する get()メソッドである。 したがってシステムは、ただ1つのモニタを CubbyHole のすべてのインスタンスと結び付ける。

class CubbyHole {
    private int seq;
    private boolean available = false;

    public synchronized int get() {
        while (available == false) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
        available = false;
        notify();
        return seq;
    }

    public synchronized void put(int value) {
        while (available == true) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
        seq = value;
        available = true;
        notify();
    }
}

CubbyHole は2つのプライベート変数を持っている。CubbyHole の現在のコンテンツになる seq と、 CubbyHole のコンテンツが検索可能かどうかを示す 論理型変数のavailable である。 available が真のとき、プロデューサが CubbyHole に新規の値を置いたばかりで、コンシューマはまだそれを消費していない。 は、コンシューマがCubbyHole の値を消費できるのは、available が真のときだけである。

CubbyHole が同期メソッドを持っているので、Java 言語ではそれぞれの CubbyHole のインスタンス(プロデューサとコンシューマが共有するものを含む)にただ1つのモニタを提供する。 制御が同期メソッドに加わる時はいつでも、メソッドを呼び出したスレッドは、メソッドが呼び出されたオブジェクトのためにモニタを獲得する。 モニタが解放されるまで、他のスレッドは同じオブジェクト上の同期メソッドを呼び出すことができない。


注釈:Java モニタは再入可能である。 つまり同じスレッドは、モニタを確保しているオブジェクトでは同期メソッドを呼び出すことができる。それによって、モニタは再度獲得される。


したがって、プロデューサが CubbyHole の put()メソッドを呼び出す時はいつでも、プロデューサはCubbyHole のモニタを獲得するので、コンシューマが CubbyHole の get()メソッドを呼び出すのを防ぐことができる。

public synchronized void put(int value) {
        // プロデューサがモニタを獲得している
    while (available == true) {
        try {
            wait();
        } catch (InterruptedException e) {
        }
    }
    seq = value;
    available = true;
    notify();
        // プロデューサがモニタを解放する
}

put()メソッドが戻る時、プロデューサはモニタを解放するので、CubbyHole のロックは解除される。

逆に、コンシューマが CubbyHole の get()メソッドを呼び出す時はいつでも、コンシューマは CubbyHole のモニタを獲得するので、プロデューサが put()メソッドを呼び出すのを防ぐことができる。

public synchronized int get() {
        // コンシューマがモニタを獲得している
    while (available == false) {
        try {
            wait();
        } catch (InterruptedException e) {
        }
    }
    available = false;
    notify();
    return seq;
        // コンシューマがモニタを解放する
}

モニタの獲得と解放は、Java 実行時システムによって自動的にまた極小に行われる。 これにより、スレッドの根本的な実装でレース状態が発生しないことが保証され、データの一貫性も保証される。


Previous | Next | Trail Map | Writing Java Programs | スレッドの制御