[予約]はJavaプログラミング・ロジック(72) - 明示的な条件

私たちは、明示的なロックを紹介するセクションこのセクションでは、その使用法と原則を紹介関連する明示的な条件を説明しています。明示的な条件はまた、我々は後に交換可能に使用することができる、条件変数、キューの状態、または条件と呼ばれることもあります。

使い方

基本的な概念と方法

スレッド間の協力メカニズムものとする問題を解決するために、競合状態のためのロック、。明示的な条件を明示的にロックsynchronzied相当に対応して通知/待ちます。/待って通知し、明示的なロックを使用した明示的な条件の使用と同期。

変数をロックする必要が明示的な条件によって作成されたロックに関連付けられた条件は、ロックインタフェースはcreateメソッドを定義します。

条件newCondition();

条件変数が示す条件は、のように定義されるインタフェースです。

コードをコピー
パブリックインターフェース条件{ 
  ボイドのawait()は例外:InterruptedExceptionをスローします。
  空awaitUninterruptibly(); 
  長いawaitNanos(ロングnanosTimeoutは)例外:InterruptedExceptionがスローされます。
  ブールのawait(長い時間、TimeUnit unit)指定例外:InterruptedExceptionをスローします。
  ブールawaitUntil(日締め切り)例外:InterruptedExceptionがスローされます。
  ボイド信号(); 
  空signalAll(); 
}
コードをコピー

notifyAll()、セマンティクスに対応するのawait())(オブジェクト・ウェイトに対応する信号()通知に対応する、signalAll()と同じです。

待って待っている時間で定義されているいくつかの方法が、いくつかのより多くの機能があり、同様の待機オブジェクトのメソッド、:

コードをコピー
//待ち時間が待ちタイムアウトを返す場合、戻り値はtrueに、そうでない場合はfalseであるため、相対的な時間である
のawait(ロング・タイム、TimeUnitでユニット)は、ブールInterruptedExceptionあるスロー; 
//待機時間が相対時間であるが、パラメータのナノ秒単位、復帰実際の値がnanosTimeoutマイナスに待機する時間である
ロングawaitNanos(ロングnanosTimeout)は例外:InterruptedExceptionを投げる; 
//待ち時間がタイムアウト戻りを待っているに、戻り値がfalseの場合はそうでない場合はtrueに、絶対的な時間である
ブールawaitUntil(日DEADLINE)例外:InterruptedExceptionがスローされます。
コードをコピー

割り込みが発生した場合、これらのメソッドは、割り込みに応答のawaitあり、InterruptedExceptionがスローされますが、割り込みフラグがクリアされます。条件はまた、割り込みへの応答を待たずにメソッドを定義します。

空awaitUninterruptibly();

この方法は、中断に終わっていないが、それが戻ったときに待機中のプロセス割り込みが発生した場合、割り込みフラグがセットされます。

一般的に、方法オブジェクトと待ち時間は、のような、のawaitメソッドを呼び出す前にロックを取得する必要は、ないロック場合、例外は、IllegalMonitorStateExceptionをスローします。キューを入力し、CPUを解放し、ロックを解除します後のawaitした後、他のスレッドが目を覚ます、または時間を待つ、または割り込み例外が発生した後、それを再取得するためにロックを取得ロックを必要とするだろうと、からメソッドを待つだろう終了します。

また、方法オブジェクトと待機、のawait復帰後、それは確かに満たすための条件を待つことになるという意味ではありません、のように、呼び出しを待つしたいが、通常はループの内側に配置され、唯一の条件は、出口に満たされています。

一般、信号/ signalAllではと通知/のnotifyAll、のような、ないロックは、例外は、IllegalMonitorStateExceptionをスローしない場合は、それらは、ロックを取得する必要があります呼び出します。信号と同じ通知は、signalAllとのnotifyAllなどは、すべての待機中のスレッドを覚ます目を覚ますためにスレッドを選ぶが、これらのスレッドは、ロック競合した後、ロックのawaitの呼び出しから復帰した後に取得されます-目覚め再する必要があります。

使用例

ReentrantLockのは、私たちは、基本的な使用条件を見て、それを通してnewCondition方法を実現しました。スレッドがアクションを実行する前に開始した後、我々は、第67 WaitThreadで同様の例を達成するため、メインスレッドの待機が実行された命令を受けた後、サンプルコードがあり、それに指示を与えるために:

コードをコピー
パブリッククラスWaitThreadは、Thread {拡張
    プライベート揮発ブール火災=偽の; 
    プライベートロックロック=新しいReentrantLockの(); 
    民間の条件条件= lock.newCondition(); 

    @Override 
    ます。public void実行(){ 
        試み{ 
            lock.lock(); 
            試す{ 
                {ながら(火!)
                    condition.await(); 
                } 
            }最後に{ 
                lock.unlock()。
            } 
            のSystem.out.println( "焼成")。
        }キャッチ(InterruptedExceptionある電子){ 
            Thread.interrupted()。
        } 
    }

    公共ボイド火災(){ 
        lock.lock()。
        {しようと
            this.fire = trueの。
            condition.signal(); 
        }最後に{ 
            lock.unlock()。
        } 
    } 

    公共の静的な無効メイン(文字列[]引数)をInterruptedExceptionある{スロー
        WaitThread waitThread =新しいWaitThreadを(); 
        waitThread.start(); 
        Thread.sleep(1000); 
        System.out.println( "火"); 
        waitThread.fire(); 
    } 
}
コードをコピー

特に注目すべきは、信号/ signalAllを混同せず、/のnotifyAllを通知する、通知/のnotifyAllメソッドがオブジェクトに定義され、条件がオブジェクト、ほとんど注意が悪用されます、例えば、上記の例における火災法のために、それがあってもよい、などそれは次のように記述されます。

コードをコピー
公共ボイド火災(){ 
    lock.lock()。
    {しようと
        this.fire = trueの。
        condition.notify(); 
    }最後に{ 
        lock.unlock()。
    } 
}
コードをコピー

このように書かれて、コンパイラは文句はありませんが、コールは通知文の中で同期化されていないので、ないIllegalMonitorStateExceptionランニングをスローします。

また、ロックとsynchronziedミックスなので、非常にのような、混乱を避けます:

コードをコピー
公共ボイド火災(){ 
    同期(ロック){ 
        this.fire = TRUE。
        condition.signal(); 
    } 
}
コードをコピー

待機/通知して同期する、覚えておいて、明示的な条件の明示的なロックフィッティング。

プロデューサ/コンシューマモデル

詩67で、我々はプロデューサ/コンシューマモデルを実現/ NOTIFY待って、我々は、通知/制限待ちを述べ、それが唯一の分析条件は非常に複雑で待っている、条件待ち行列を持つことができます。プロデューサ/コンシューマモデルでは、実際には、完全なキュー、空に関連するキューに関連付けられた2つの条件があります。明示的なロック状態を使用すると、複数のキューを作成することができます。ここでは、明示的なロック/前記キューコードを遮断する条件で再実装を使用します。

コードをコピー
静的クラスMyBlockingQueue <E> { 
    プライベートキュー<E>キュー= NULL; 
    プライベートint型の制限; 
    プライベートロックロック=新しいReentrantLockの(); 
    民間条件notFull = lock.newCondition(); 
    民間条件notEmpty = lock.newCondition(); 


    公共MyBlockingQueue(INT限界){ 
        this.limit =リミット。
        キュー=新しいArrayDeque <>(上限)。
    } 

    公共ボイドPUT(E e)はInterruptedExceptionある{スロー
        lock.lockInterruptiblyを(); 
        {試みる
            一方(queue.size()==限界){ 
                notFull.awaitを(); 
            } 
            queue.add(E); 
            notEmpty.signal();    
        }最後に{ 
            lock.unlock()。
        } 
    } 

    パブリックEテイク()はInterruptedExceptionある{スロー
        lock.lockInterruptiblyを(); 
        試す{ 
            (queue.isEmpty()){ながら
                notEmpty.await()。
            } 
            E E = queue.poll()。
            notFull.signal(); 
            電子を返します。    
        }最後に{ 
            lock.unlock()。
        } 
    } 
}
コードをコピー

待機定義二つの条件:キューがいっぱいの場合は、キューが空の場合は、プロセスに入れる少ない(notFull)より、(notEmpty)空ではないが、、その後、notFull待ちは、プロセスに取る、notEmpty、put操作で待機しています通知notEmpty後、操作後通知notFullを取ります。

このように、コードが不要なウェイクアップ回避しながら、より読みやすいと効率性をチェックし、改善します。JavaおよびクラスArrayBlockingQueueでの契約は、同様の方法を採用していました。

原理は
ConditionObjectの
概念と明示的な条件の使用状況を理解し、我々はそれを見てどのようnewCondition()のコードがあるReentrantLockのを、達成することです。

パブリック条件newCondition(){ 
    戻りsync.newCondition()。
}

内部同期は、ReentrantLockのクラスオブジェクトnewCondition()コードです。

最終ConditionObjectのnewCondition(){ 
    リターン新しいConditionObjectの()。
}

ConditionObjectのは、AQS定義された内部クラスで、空想は、AQSセクションを理解してご覧ください。ConditionObjectの実装は、より複雑である、我々は、実装の原則を議論し、簡単にキーコードの一部を通過しました。内部ConditionObjectのは、キューがそのメンバー宣言した状態を表して待って、待ち行列があります。

//ヘッドノードキュー条件
プライベートfirstWaiterノード過渡; 
//条件キューテール・ノード
のプライベート過渡ノードlastWaiter。

AQS ConditionObjectの内部クラスのメンバーである、そのような待ちキューAQSロックに定義されているように、直接AQSにデータにアクセスすることができます。

私たちは、のawaitメソッドを見て達成するために、いくつかの方法を見てください。

await実現分析

ここでは、コメントを追加することにより、基本的な考え方を説明し、コードのawait方法です。

コードをコピー
最終的な空のawait国民は()例外:InterruptedException {スロー
    //割り込みフラグの最初のウェイトが設定されている場合は、直接のスロー例外
    IF(Thread.interrupted())
        スロー新しい新しい例外:InterruptedExceptionを(); 
    // 1.現在のスレッドのためにノードを作成し、条件に参加キュー
    ノードノードaddConditionWaiter =(); 
    // 2に保持されたロック解除
    INT = savedState fullyRelease(ノード); 
    INT interruptMode = 0; 
    それが中断され、またはtrueにisOnSyncQueueとなるCPUアップ、まで待機// 3.ギブ
    /他のスレッドがキュー状態に待機する/ isOnSyncQueueノードが真
    //が待っている条件が満たされている、ロックキューの外に移動し
    ながら、{(!isOnSyncQueue(ノード))
        LockSupport.park(この); 
        IF((interruptMode = checkInterruptWhileWaiting !(ノード))= 0)
            BREAK; 
    } 
    // 4.再獲得ロック
    IF(acquireQueued(ノード、savedState)&& interruptMode = THROW_IE!)
        interruptMode = REINTERRUPT; 
    IF(!node.nextWaiter = NULL)//クリーンアップIFキャンセル
        unlinkCancelledWaiters(); 
    // 5.割り込み処理、例外や割り込み設定投げますフラグ
    IF(interruptMode!= 0)
        reportInterruptAfterWait(interruptMode); 
}
コードをコピー

awaitNanosは、分析を実現します

awaitNanosは実現が実質的に類似してお待ちしており、主な違いは次のように待機時間が定義されています。

コードをコピー
最終的なロングawaitNanos公開(ロングnanosTimeout)はInterruptedExceptionある{スロー
    IF(Thread.interrupted())
        ;スロー新しい新InterruptedExceptionある()
    ノードノードaddConditionWaiter =(); 
    int型= savedState fullyRelease(ノード); 
    System.nanoTimeのに長いlastTime =(); 
    int型= 0 interruptMode; 
    一方{(!isOnSyncQueue(ノード))
        IF(nanosTimeout <= 0L){ 
            //待ちタイムアウト、キュー待機外部ロックの待機状態にキューからノード
            transferAfterCancelledWait(ノード); 
            BREAK; 
        } 
        //定義最大時間
        LockSupport.parkNanos(これ、nanosTimeout); 
        IF((interruptMode = checkInterruptWhileWaiting(ノード))!= 0)
            BREAK;

        長い今= System.nanoTimeの(); 
        //计算下次等待的最长时间
        nanosTimeout - =今- lastTime。
        lastTimeは今=。
    } 
    IF(acquireQueued(ノード、savedState)&& interruptMode = THROW_IE!)
        interruptMode = REINTERRUPT。
    (もし!node.nextWaiter =ヌル)
        unlinkCancelledWaiters(); 
    (!interruptMode = 0)の場合
        reportInterruptAfterWait(interruptMode)。
    リターンnanosTimeout - (System.nanoTimeの() - lastTime)。
}
コードをコピー

達成するための信号解析

コード信号方式:

コードをコピー
最終的な無効信号パブリック(){ 
    //現在のスレッドがロックを保持している検証
    IF(isHeldExclusively()!)
        スロー新しい新は、IllegalMonitorStateException(); 
    //モーニングコールdoSignalキュー待機中に最初のスレッド
    のノードまず= firstWaiter; 
    IF(=ファースト!ヌル)
        doSignal(最初の); 
}
コードをコピー

doSignalコードの基本的なロジックである、列挙されていません。

  1. ロック状態の待機キューからノードへの待ち行列
  2. モーニングコールLockSupport.unparkスレッド

概要

このセクションでは、使用状況や実施原則の明示的な条件を説明します。コードがない通知/使用のnotifyAllとして誤って書き込まれた信号/ signalAllに慎重に、より効率的で、より読みやすい、条件が複数のキューをサポートすることができる、待機/通知と比較し、明示的なロックと組み合わせて使用​​されます。

70日目からは、このセクションに、私たちはJavaとの契約の基本紹介 - これらに基づいて、アトミック変数およびCAS、明示的なロックと条件を、Javaとの契約も、高レベルのデータ構造、ツールやサービスを利用するために多くの簡単提供しています次のセクションの先頭から、のは、いくつかの同時データ構造を探索しましょう。

おすすめ

転載: www.cnblogs.com/ivy-xu/p/12364391.html