Java クラス ライブラリに適切な同期ツールが提供されていない場合は、カスタム同期ツールを構築する必要があります。
状態依存の操作をブロックするための構造
次のようにコードをコピーします。
オブジェクト状態のロックを取得;//ロックの取得を要求する
while(前提条件が成立しない){//前提条件が満たされていません
release lock;//最初にロックを解放します
前提条件が成立するまで待つ;//前提条件が成立するまで待つ
中断またはタイムアウトが発生した場合はオプションで失敗します;//中断またはタイムアウトにより実行が失敗しました
reacquire lock;//ロックの取得を再試行します
}
アクションを実行//実行
ロックを解除;//ロックを解除
境界付きキャッシュ実装の基本クラスの例
次のようにコードをコピーします。
パブリック クラス BaseBoundBuffer<V> {
プライベート最終 V[] バッファ;
プライベート int テール;
プライベート int ヘッド。
プライベート int カウント;
@SuppressWarnings("未チェック")
public BaseBoundBuffer(int 容量) {
buf = (V[]) 新しいオブジェクト[容量];
}
public synchronized void doPut(V v) {
buf[tail] = v;
if (++tail == buf.length)
尾部 = 0;
カウント++;
}
パブリック同期 V doTake() {
V v = buf[ヘッド];
if (++head == buf.length)
ヘッド = 0;
カウント - ;
v を返します。
}
public Final synchronized boolean isFull() {
戻り値 == buf.length;
}
public Final synchronized boolean isEmpty() {
戻り値 == 0;
}
}
ブロッキング実装方法 1: 呼び出し元に例外をスローする
次のようにコードをコピーします。
public synchronized void put1(V v) throws Exception{
if(isFull())
throw new Exception("完全なエラー");
doPut(v);
}
分析: 例外が発生した場合は例外を使用する必要があります。ここで例外をスローするのは適切ではありません。呼び出し元は前提条件が満たされない状況を処理する必要があり、これでは根本的な問題は解決されません。
ブロッキングの実装方法 2: ポーリングとスリープによる
次のようにコードをコピーします。
public void put2(V v) throws InterruptedException {
while (true) {//ポーリング
同期された (これ) {
if (!isFull()) {
doPut(v);
戻る;
}
}
Thread.sleep(SLEEP_TIME);//スリープ
}
}
分析: スリープ時間の SLEEP_TIME 設定を評価するのは困難です。設定が小さすぎると、CPU が複数回ポーリングする可能性があり、より多くの CPU リソースが消費されます。設定が大きすぎると、応答性が低下します。
ブロッキング実装方法 3: 条件付きキュー
条件キュー内の要素は、関連する条件を 1 つずつ待機するスレッドです。各Javaオブジェクトはロックとして使用でき、各オブジェクトは条件キューとしても使用でき、Objectのwait、notify、notifyAllメソッドが内部条件キューのAPIを構成します。 Object.wait は自動的にロックを解放し、他のスレッドがロックを取得してオブジェクトの状態を変更できるように、現在のスレッドを一時停止するようにオペレーティング システムに要求します。 Object.notify と Object.notifyAll は、待機中のスレッドをウェイクアップし、条件キューからウェイクアップするスレッドを選択して、ロックの再取得を試みることができます。
次のようにコードをコピーします。
public synchronized void put3(V v) throws InterruptedException {
while(isFull())
待って();
doput(v);
すべて通知();
}
分析: 応答性が向上し、シンプルで使いやすくなります。
条件付きキューを使用する
1. 条件述語
1).定義: 条件付き述語は、操作が状態依存操作になるための前提条件です。条件述語は、クラス内の個々の状態変数で構成される式です。たとえば、put メソッドの条件述語は「キャッシュが空ではない」です。
2).関係: 条件付き待機には、ロック、待機メソッド、条件付き述語を含む重要な三項関係があります。条件述語には複数の状態変数が含まれており、各状態変数はロックで保護する必要があるため、条件述語をテストする前にロックを保持する必要があります。ロック オブジェクトと条件付きキュー オブジェクト (および待機メソッドと通知メソッドが呼び出されるオブジェクト) は、同じオブジェクトである必要があります。
3) 制約: 待機する各呼び出しは、特定の条件述語に暗黙的に関連付けられます。特定の条件述語が呼び出されるとき、呼び出し元は、その条件キューに関連するロックをすでに保持している必要があります。このロックは、コンポーネントの条件述語も保護する必要があります。状態変数
2. 条件付きキューの使用ルール
1) 通常、条件付き述語があります。
2). wait を呼び出す前に常に条件述語をテストし、wait から戻った後に再度テストします。
3).ループ内で常に wait を呼び出します。
4) 条件述語を構成する状態変数がロックによって保護されていることを確認し、このロックを条件キューに関連付ける必要があります。
5) wait、notify、notifyAll を呼び出すときは、条件キューに関連付けられたロックを保持する必要があります。
6) 条件付き述語をチェックした後、保護されたロジックの実行を開始する前にロックを解除しないでください。
3.通知
nofify の代わりに、notifyAll を使用してみてください。 nofify は、スレッドを休止状態からブロック状態にランダムにウェイクアップするためです (ブロック状態とは、常にロックを取得しようとしているスレッドです。つまり、ロックが使用可能であることが判明すると、ロックが解除されます)。ロックを即座に保持します)、一方、notifyAll は、条件キュー内のすべてのスレッドを休止状態からブロック状態にウェイクアップします。スレッド A が条件述語 Pa により休止状態に入り、スレッド B が休止状態になった場合を考えてみましょう。このとき、条件述語 Pb により休止状態になります。が true の場合、スレッド C は 1 つの通知を実行します。JVM がウェイクアップするスレッド A をランダムに選択すると、スレッド A は条件述語 Pa が true でないことを確認してからスリープ状態に入り、それ以降、他のスレッドはウェイクアップできなくなります。を使用すると、プログラムは常にスリープ状態になります。 NoticeAll は異なります。JVM は、条件述語が true でないためにスレッドがランダムに選択されてスリープ状態になった場合でも、条件キュー内の待機中のすべてのスレッドをスリープ状態からウェイクアップします。実行を続行します。
4. 状態依存メソッドの標準形式のコピーコードは次のとおりです。
void stateDependentMethod throwsInterruptedException{
同期(ロック){
while(!条件述語))
ロック.wait();
}
//何かをする();
....
すべて通知();
}
条件オブジェクトの表示
明示的な Condition オブジェクトは、より柔軟な代替手段であり、より豊富な機能を提供します。各ロックに複数の待機が存在でき、条件付き待機は割り込み可能または割り込み不可能にすることができ、時間ベースの待機、および公平または不公平なキュー操作が可能です。条件キューが組み込みロックに関連付けられているのと同じように、条件はロックに関連付けることができます。 Condition を作成するには、関連付けられた Lock で Lock.newCondition メソッドを呼び出します。次のコードは、表示条件変数を使用して境界付きキャッシュを再実装するために使用されます。
パブリック クラス ConditionBoundedBuffer<V> {
プライベート最終 V[] バッファ;
プライベート int テール;
プライベート int ヘッド。
プライベート int カウント;
private Lock ロック = new ReentrantLock();
プライベート条件 notFullCondition = lock.newCondition();
プライベート条件 notEmptyCondition = lock.newCondition();
@SuppressWarnings("未チェック")
public ConditionBoundedBuffer(int 容量) {
buf = (V[]) 新しいオブジェクト[容量];
}
public void doPut(V v) throws InterruptedException {
試す {
ロック.ロック();
while (count == buf.length)
notFullCondition.await();
buf[tail] = v;
if (++tail == buf.length)
尾部 = 0;
カウント++;
notEmptyCondition.signal();
} ついに {
ロック解除();
}
}
public V doTake() は InterruptedException をスローします {
試す {
ロック.ロック();
while (カウント == 0)
notEmptyCondition.await();
V v = buf[ヘッド];
buf[ヘッド] = null;
if (++head == buf.length)
ヘッド = 0;
カウント - ;
notFullCondition.signal();
v を返します。
} ついに {
ロック解除();
}
}
}