3.1 アクティブオブジェクト

Active オブジェクトは、メソッドの実行スレッドを、メソッドが呼び出されたスレッドから分離する設計パターンです。このパターンの目的は、非同期メソッド呼び出しと要求処理スケジューラを使用して並列実行を提供することです。

簡易版:

アクティブなオブジェクト

古典的なバリエーション:

アクティブオブジェクト2

このテンプレートには 6 つの要素があります。

  • クライアントのパブリック メソッドへのインターフェイスを提供するプロキシ オブジェクト。
  • アクティブなオブジェクトのアクセス方法を定義するインターフェイス。
  • クライアントから受信したリクエストのリスト。
  • クエリを実行する順序を決定するスケジューラ。
  • アクティブオブジェクトメソッドの実装。
  • クライアントが結果を受け取るためのコールバック プロシージャまたは変数。

3.2 ロック

ロック パターンは、複数のスレッド間で共有リソースへの排他的アクセスを可能にする同期メカニズムです。ロックは、同時実行制御ポリシーを強制する 1 つの方法です。

基本的に、各スレッドが対応する共有リソースにアクセスする前に「ロックの取得」を試みることを前提として、ソフト ロックが使用されます。

ただし、一部のシステムでは、アクセスを試みたスレッドで例外をスローすることにより、ロックされたリソースへの不正アクセスの試みが中止される強制ロック メカニズムを提供しています。

セマフォは最も単純なタイプのロックです。データ アクセスに関しては、共有 (読み取り専用) または排他的 (読み取り/書き込み) のアクセス モードの区別はありません。共有モードでは、複数のスレッドが読み取り専用モードでデータにアクセスするためのロックを要求できます。排他的アクセス モードは、更新アルゴリズムと削除アルゴリズムでも使用されます。

ロックパターン

ロックの種類は、スレッドの実行の継続をブロックする戦略によって区別されます。ほとんどの実装では、ロックを要求すると、ロックされたリソースが使用可能になるまでスレッドの実行が続行されなくなります。

スピンロックは、アクセスが許可されるまでループ内で待機するロックです。このようなロックは、スレッドがロックを短時間待機する場合に非常に効率的であるため、スレッドの過剰な再スケジュールが回避されます。いずれかのスレッドが長時間ロックを保持すると、アクセスを待機するコストが大幅に増加します。

ロックパターン2

ロック メカニズムを効果的に実装するには、ハードウェア レベルでのサポートが必要です。ハードウェア サポートは、「テスト アンド セット」、「フェッチ アンド 追加」、または「比較 アンド スワップ」などの 1 つ以上のアトミック操作として実装できます。このような命令により、ロックが空いているかどうかを中断することなく確認し、空いている場合にはロックを取得できます。

3.3 モニター

モニター パターンは、共有リソースへのアクセスを提供する高レベルのプロセス対話および同期メカニズムです。共通のリソース (通常はハードウェアまたは一連の変数) を使用して、2 つ以上のコンピューター タスクを同期するアプローチ。

モニターベースのマルチタスクでは、コンパイラーまたはインタープリターは、プログラマーに対して透過的に、適切にフォーマットされたルーチンにロック/ロック解除コードを透過的に挿入するため、プログラマーは同期プリミティブを明示的に呼び出す必要がなくなります。

モニターは次のもので構成されます。

  • 共有リソースと対話する一連のプロシージャ
  • ミューテックス
  • このリソースに関連付けられた変数
  • 競合状態を回避するための条件を定義する不変式

モニター・プロシージャーは作業を開始する前にミューテックスを取得し、プロシージャーが終了するか特定の条件が待機するまでそれを保持します。ミューテックスを解放する前に各プロシージャが不変条件が true であることを保証する場合、競合状態ではタスクはリソースを取得できません。

これは、 Java でおよびメソッドを使用して同期オペレータがどのように動作するかです。wait()notify()

3.4 ダブルチェックロック

二重チェックされたロックは、ロック取得のオーバーヘッドを削減することを目的とした並列設計パターンです。

まず、同期を行わずにブロッキング状態がチェックされます。チェックの結果、ロックを取得する必要があることが示された場合にのみ、スレッドはロックの取得を試みます。

//Double-Checked Locking
public final class Singleton {
private static Singleton instance; //Don't forget volatile modifier

public static Singleton getInstance() {
     if (instance == null) {                //Read

         synchronized (Singleton.class) {    //
             if (instance == null) {         //Read Write
                 instance = new Singleton(); //
             }
         }
     }
 }

スレッドセーフな環境でシングルトン オブジェクトを作成するにはどうすればよいですか?

public static Singleton getInstance() {
   if (instance == null)
    instance = new Singleton();
}

異なるスレッドからシングルトン オブジェクトを作成する場合、複数のオブジェクトが同時に作成される状況が発生する可能性がありますが、これは容認できません。したがって、オブジェクトの作成を synchronized ステートメントでラップするのが合理的です。

public static Singleton getInstance() {
    synchronized (Singleton.class) {
        if (instance == null)
        instance = new Singleton();
    }
}

このアプローチは機能しますが、小さな欠点があります。オブジェクトが作成された後、それ以降そのオブジェクトを取得しようとするたびに、同期ブロック内でチェックが実行されます。これは、現在のスレッドとそれに接続されているすべてのものがロックされることを意味します。したがって、このコードは少し最適化できます。

public static Singleton getInstance() {
     if (instance != null)
        return instance;

    synchronized (Singleton.class) {
        if (instance == null)
        instance = new Singleton();
    }
}

一部の言語および/または一部のマシンでは、このパターンを安全に実装することができません。したがって、アンチパターンと呼ばれることもあります。このような機能により、Java メモリ モデルと C++ メモリ モデルに「前に起こる」という厳密な順序関係が出現します。

これは通常、シングルトン設計パターンなどのマルチスレッド プログラムで遅延初期化を実装するオーバーヘッドを削減するために使用されます。変数の遅延初期化では、変数の値が計算で必要になるまで初期化が延期されます。

3.5 スケジューラ

スケジューラは、スケジューリング ポリシーを実装するためのメカニズムを提供する並列設計パターンですが、特定のポリシーからは独立しています。待機中のスレッドの順序を明示的に指定するオブジェクトを使用して、スレッドがシーケンシャル コードを実行する順序を制御します。