私たちは、このセクションは、セクション67および/私たちが言及した基本的な待ち時間を利用実現に通知されるいくつかの基本的な調整メカニズムの糸のセクション68に実現し、Javaとの契約は、いくつかの特別な同期ツールを持って、我々は探求しようとしていますそれら。
私たちは、ツールが含ま探検したいです:
- 読み書きロックReentrantReadWriteLock
- セマフォセマフォ
- カウントダウンボルトたCountDownLatch
- フェンスCyclicBarrierをサイクリング
表示部71と同様の条件で説明した表示部72をロック、それらが基づいている、AQS AQSは、セクション71を参照して達成することができます。特定の同期コラボレーション・シナリオでは、待機/通知、表示ロック/条件の最も基本的なの使用に比べて、彼らはより便利で効率的です。ここでは、その使用、目的や根拠の基本的な概念を説明します。
読み書きロックReentrantReadWriteLock
我々は、同期部66は、説明し、2つのロックセクションを導入する前に、71セクションは表示ロックReentrantLockのを説明しています。同じそれが読まれているかどうかを、保護オブジェクトへのアクセス、または書き込みのために、彼らは同じロックを求めます。いくつかのシナリオでは、それはとても並列に操作が大幅にパフォーマンスを向上させることができ読んで、複数のスレッドがシーンを書いてどのくらい読んで、並行して読むことができ、必要ありません。
どのようにそれの一貫性に影響を与えることなく、並行して読み取り操作をさせることができますか?答えは、読み書きロックを使用することです。Javaでは、界面での契約は書き込みロックReadWriteLockを表し、主に実装するクラスはリエントラント読み書きロックReentrantReadWriteLockです。
ReadWriteLockは、次のように定義されます。
パブリックインターフェースReadWriteLock { ロックreadLock(); ロックWRITELOCK(); }
A ReadWriteLockは、2つのロック、読み取りロック、書き込みロックによって生成さ。読むの用途はロックと書き込みロックを使用して書き込み操作をお読みください。
「書き込み - 書き込みは」できない - 操作は、「読み書き」、並行することができます - 唯一の「読み読み」ことに留意すべきです。唯一つのスレッドは、他のスレッドがロックを取得することはできません、書き込みロックを保持しながら、書き込みロックを取得する際に、任意のロックを保持しているだけどのスレッドのみ、取得することができない、書き込むことができます。他のスレッドが書き込みロックケースを保持していないでは、複数のスレッドがロックを取得して保持することができます。
次のようにReentrantReadWriteLockは、2つのコンストラクタを持っているリエントラント読み書きロック、次のとおりです。
公共ReentrantLockの() 公共ReentrantLockの(ブールフェア)
火災はその後、公正は、合格していないかどうかを示し偽、同様の意味を明示的に、我々は詳細には触れませんプレゼンテーションをロックです。
次のように私たちは、簡単な例を取り上げ、ReentrantReadWriteLock MyCacheというクラスを達成するために、バッファの使用は、コードは次のとおりです。
パブリッククラスMyCacheという{ プライベート地図<文字列、オブジェクト>マップ=新しいHashMapの<>(); プライベートReentrantReadWriteLock readWriteLock =新しいReentrantReadWriteLock(); プライベートロックreadLock = readWriteLock.readLock(); プライベートロックWRITELOCK = readWriteLock.writeLock(); パブリックオブジェクトのget(文字列のキー){ readLock.lock(); {試します map.get(キー)を返します。 } 最後に { readLock.unlock(); } } パブリックオブジェクトのPUT(文字列のキー、オブジェクト値){ writeLock.lock(); {試します map.put(キー、値)を返します。 } 最後に { writeLock.unlock(); } } (クリアます。public void){ writeLock.lock(); {試します map.clear(); } 最後に { writeLock.unlock(); } } }
コードは、詳細には触れていない、比較的簡単です。
読み書きロックは、それを達成する方法ですか?読み取りと書き込みロックは、それらがどのように調整されるか、2つのロックのように見えますか?具体的な実装では、我々はそのアイデアを概説、より複雑です。
内部的には、それらは、変数を使用して、リードにロック16、同じ整数の変数がロック状態を示している16ビットと書き込みロックを使用するCAS動作を容易にする、ロックが実際に唯一のキューです。
書き込みロック取得は、現在待つそうでない場合は、他のスレッドのロックを保持しないようにすることです。書き込みロックが解除された後、あること、キュー最初のスレッドで待機して、目を覚ます、それは書き込みロックのために待機している読み取りロックを待機するかもしれません目を覚まします。
読み取りロックの獲得は、また、読み込みロックを取得した後、それは待ち行列をチェックします。まず、ちょうど書き込みロックが保持されていない、読み取りロックを得ることができ、同じではありません待機中のスレッドの一番上に1つは、読み取りロックをきっかけ、書き込みロックスレッドまで待ちます。ある場合は、他のスレッドは読み取りロックが待機する書き込みロックの取得を保持しています。ロックの読み取りが解放された後、場合読み取りロックと書き込みロックの数が0に変更されているチェックし、もしそうであれば、キュー内で待機している次のスレッドを覚まします。
セマフォセマフォ
ロック前の制限の導入は、同時にリソースにアクセスできる唯一のスレッドです。現実には、多くの場合、複数のリソースがあるが、同時に各缶はアクセスのみに一つのスレッド、例えば、ホテルのダイニングテーブル、電車のトイレも。同時にアクセスすることが可能であっても単一のリソースいくつかが、複数の同時アクセスの数は、パフォーマンスに影響を与える可能性があるので、うまくいけば、スレッドの同時アクセスの数を制限します。他の例では、許可、およびアカウンティングソフトウェアを使用してアカウントの異なるレベル、異なる限界まで同時アクセスの最大数を関連。
セマフォクラスセマフォは、このような問題を解決するために使用され、それがリソースへの同時アクセス数を制限することができ、それは2つのコンストラクタがあります。
公共セマフォ(int型の許可) 公共セマフォ(int型の許可、ブールフェア)
火は、公正を表し、以前の類似したの意味を説明し、許可は、ライセンスの数を示しています。
セマフォ方法はライセンスとリリース許可証を取得するために、二つのタイプがあり、mainメソッドロックに似ている、主な方法は以下のとおりです。
//閉塞はライセンスを取得するには 公共のボイド取得()は例外:InterruptedExceptionをスローします //ライセンスを取得遮断し、しない割り込み応答 ます。public void acquireUninterruptibly() //バルク複数のライセンスを取得 公共のボイド取得(int型の許可は)例外:InterruptedExceptionをスローします ます。public void acquireUninterruptibly(int型の許可) //取得してください パブリックブールtryAcquire() //取得するために待機している時間を定義します パブリックブールtryAcquire(int型の許可、long timeout、TimeUnit unit)指定例外:InterruptedExceptionをスローします //リリースライセンス ます。public voidリリース()
私たちは、簡単な例を見て、アクセスする同時ユーザーの数を制限し、次のように、100を超えることはできません。
パブリッククラスAccessControlService { パブリック静的クラスConcurrentLimitExceptionはのRuntimeExceptionを{延び プライベート静的最終長いserialVersionUIDの= 1L; } プライベート静的最終int型のMAX_PERMITS = 100; プライベートセマフォ許可=新しいセマフォ(MAX_PERMITS、真の); パブリックブールログイン(文字列名、文字列のパスワード){ (もし!permits.tryAcquire()){ //ログ同時ユーザーの数が制限を超えました 新しいConcurrentLimitExceptionを投げます(); } // ..他の検証 trueを返します。 } 公共のボイドログアウト(文字列名){ permits.release(); } }
コードは、詳細には触れていない、比較的簡単です。
我々値許可が1に設定されている場合、あなたはそれが一般的なロックになっていることと思うかもしれないが、それは一般的なロックと異なっていることに留意すべきです。一般的なロックは、ロックを保持しているスレッドによって解放することができますが、唯一の1つのライセンス数セマフォは、いずれかのスレッドがそれらの放出のメソッドを呼び出すことができ、示されました。メイン実装クラスReentrantLockのロックがリエントラントですが、セマフォは、すべてのコールの消費がライセンスを取得し、例えば、次のコードセグメントが表示されません。
セマフォは、新しいセマフォ(1)=が可能になります。 permits.acquire(); permits.acquire(); System.out.println( "買収");
通話の取得をブロックします2番目のプログラムでは、決して出力が「取得します」。
基本的な原理は単純でセマフォあり、また、AQSの認識に基づいて、許可が共有ロックの数を示す、取得方法は、ロック数が0より大きいチェックマイナス1より大きい、成功するために、そうでない場合は待機し、ロックを解除することで、Aでありますプラス数は、待機中のスレッドを目覚め。
カウントダウンボルトたCountDownLatch
私たちは、/ 68のウェイトを使用実装に私たちが言及したシンプルなボルトMyLatchを通知し、Javaとの契約はすでに同様のツールを提供し、たCountDownLatchです。これは、スレッドがドアを待っているすべての希望は、その後、カウントダウン、0にカウントダウン、オープンボルトを始め、開始がオフになっている、それはボルトに相当し、意味がある、おそらくですが、すべてのスレッドが待っています開口部を閉じることができない後に使い捨てである缶、。
たCountDownLatchカウントがあり、カウントがコンストラクタを介して渡されます。
パブリックたCountDownLatch(int型の数)
複数のスレッドがこの数に共同作業を行うことができ、それが主な方法があります:
公共のボイドのawait()は例外:InterruptedExceptionをスローします パブリックブールのawait(long timeout、TimeUnit unit)指定例外:InterruptedExceptionをスローします ます。public void COUNTDOWN()
カウントが0であるか否かのawait()をチェックし、0以上であれば、待機、のawait()は中断されていてもよい、最大待ち時間を設けてもよいです。カウントダウンチェックインカウンター、あなたが直接返します0を持っている場合は、新しいカウントが0になった場合、そうでなければ、すべての待機中のスレッドを覚ますそして、カウントを減らします。
68で、我々は2つのシナリオボルトを説明し、一つが同時に開始され、他のマスター・スレーブのコラボレーションです。彼らは、スレッドの2種類があり、相互に、我々は次のたCountDownLatchの再プレゼンテーションを使用し、同期する必要があります。
同時に開始シナリオで、スレッド開始指示信号にスレッドを待っている審判の実行は、発行された後、すべての選手が同時にスレッドを開始発行され、最初のカウントが1である、のawaitの呼び出し選手スレッド、メインスレッドがカウントダウン、次のサンプルコードを呼び出します。
パブリッククラスRacerWithCountDownLatch { 静的クラスレーサー{スレッドを拡張します たCountDownLatchラッチ; パブリックレーサー(たCountDownLatchラッチ){ this.latch =ラッチ; } @オーバーライド ます。public void実行(){ {試します this.latch.await(); System.out.println(のgetName() + +のSystem.currentTimeMillis()) "を実行を開始します"。 }キャッチ(InterruptedExceptionある電子){ } } } 公共の静的な無効メイン(文字列[]引数)が例外:InterruptedExceptionをスロー{ int型NUM = 10; たCountDownLatchラッチ=新しいたCountDownLatch(1)。 レーサー=新しいスレッド[]スレッド[NUM]。 {(I ++; I <NUM iが0 = INT)のために レーサー[I] =新しいレーサー(ラッチ)。 レーサー[I] .start(); } Thread.sleep(1000); latch.countDown(); } }
コードは、詳細には触れていない、比較的簡単です。コラボレーションからマスターモードでは、メインスレッドの結果は、時間、スレッドは作業の終了を待つ必要があり、作業スレッドに依存し、ワーカースレッドの数のカウント初期値は、スレッドは仕事の後にカウントダウンを呼び出して、メインスレッドの呼び出しは待機サンプルコードを待ちます次のように:
パブリッククラスMasterWorkerDemo { 静的クラスワーカースレッド{延び たCountDownLatchラッチ; 公共労働者(たCountDownLatchラッチ){ this.latch =ラッチ; } @オーバーライド ます。public void実行(){ {試します タスクに//シミュレート作業 Thread.sleep((int型)(Math.random()* 1000)); //シミュレート例外 IF(Math.random()<0.02){ 新しいのRuntimeException(「不運」)を投げます。 } }キャッチ(InterruptedExceptionある電子){ } 最後に { this.latch.countDown(); } } } 公共の静的な無効メイン(文字列[]引数)が例外:InterruptedExceptionをスロー{ int型workerNum = 100; たCountDownLatchラッチ=新たCountDownLatch(workerNum)。 ワーカー[]労働者=新しいワーカー[workerNum]。 {(; I <workerNum I ++ INTがI = 0)するため 研究者[I] =新しいワーカー(ラッチ)。 研究者[I] .start(); } latch.await(); System.out.println( "コレクトワーカー結果"); } }
ここで、通話がその異常がワーカースレッドで発生した場合に、メインスレッドがコールのawaitから復帰することができ呼び出されることを確認するためにカウントダウンfinally文を置かれるべきであることを強調すべきです。
フェンスCyclicBarrierをサイクリング
私たちは、/ 68のウェイトを使用実装に私たちが言及したシンプルなランデブーAssemblePointを通知し、Javaとの契約はすでに同様のツールを提供し、CyclicBarrierをです。それは意味がそれがフェンスに相当し、あるおそらくですが、すべてのスレッド他のスレッドを待つフェンス必要性に到達し、そしてすべてのスレッドが一緒にそれまでに到達した後、それは周期的で、二重化同期として使用することができます。
並列反復計算のために特に適しCyclicBarrierをは、各スレッドは、計算の一部を担当し、その後、他のスレッドのフェンスを完了するために、場所内のすべてのスレッドは、データ及び計算結果の交換、次の反復のために待機しています。
そしてたCountDownLatchは、同様に、それはまた、番号を持っていますが、コンストラクタを介して渡され、この図に参加するスレッドの数を表します。
公共CyclicBarrierを(int型のパーティー)
これは次のように、また、Runnableをパラメータを取るコンストラクタです。
公共CyclicBarrierを(int型のパーティー、RunnableをbarrierAction)
このパラメータは、すべてのスレッドで次のアクションを実行する前に、アクションの動作パラメータは、フェンスによって、このアクションは、最後のスレッドの実行に到達するために、すべてのスレッドがバリアに到達フェンスアクションを、示しています。
CyclicBarrierを主な方法は待つことです。
公共int型のawait()は例外:InterruptedException、BrokenBarrierExceptionをスローします 公共int型のawait(long timeout、TimeUnit unit)指定InterruptedExceptionある、BrokenBarrierException、TimeoutExceptionがスローされます
await他のスレッドを待ってフェンスに到達、待つために呼び出した後、彼はその後に内部同期カウントをリセットし、彼はオプションのコマンドの実装に到着する最後のものである場合には、実行した後、すべての待機中のスレッドを覚ます、到着していると述べましたリサイクル。
await中断することができ、あなたは最大待機時間を定義することができ、またはタイムアウト割り込み後に例外がスローされます。フェンスを表し異常BrokenBarrierExceptionが、破壊されることに留意すべきで、その平均値を何?CyclicBarrierをでは、スレッドはスレッドのコールが待つたびに中断、またはタイムアウトさ互いに関与影響され、フェンスは、フェンスは、フェンスが破壊される場合に加えて、アクションが例外をスローし、破壊されます破壊された後、すべてのスレッドのawait、スローBrokenBarrierExceptionを呼び出して終了します。
私たちは、簡単な例を見て、セットでそれぞれ複数のスレッドの訪問者は、AとBの同期ポイント:
パブリッククラスCyclicBarrierDemo { 静的クラスツーリストは{スレッドを拡張します CyclicBarrierをバリア; 公共ツーリスト(CyclicBarrierをバリア){ this.barrier =バリア; } @オーバーライド ます。public void実行(){ {試します //アナログは独立して動作します Thread.sleep((int型)(Math.random()* 1000)); // Aランデブー barrier.await(); System.out.println(this.getName()+ "Aが到着しました" +のSystem.currentTimeMillis())。 //シミュレーションの別のセットは、独立して実行された後、 Thread.sleep((int型)(Math.random()* 1000)); //ポイントBのセット barrier.await(); System.out.println(this.getName()+ "到着B" +のSystem.currentTimeMillis())。 }キャッチ(InterruptedExceptionある電子){ }キャッチ(BrokenBarrierException電子){ } } } パブリック静的無効メイン(文字列[] args){ int型NUM = 3; 観光[]スレッド=新しい観光[NUM]。 CyclicBarrierをバリア=新しいCyclicBarrierを(NUM、新しいRunnableを(){ @オーバーライド ます。public void実行(){ System.out.println( "すべての到着" +のSystem.currentTimeMillis() 。+ +にThread.currentThread()のgetName()) "によって実行されます"。 } }); {(I ++; I <NUM iが0 = INT)のために スレッド[I] =新しいツーリスト(バリア)。 スレッド[I] .start(); } } }
私のコンピュータで最初の出力は、次のとおりです。
スレッド1で実行されるすべての到着1490053578552 スレッド-1 A 1490053578555到着 スレッド-2 A 1490053578555到着 スレッド0 A 1490053578555到着 スレッド0で実行されるすべての到着1490053578889 スレッド0着いB 1490053578890 スレッド-2到着したB 1490053578890 スレッド-1到着したB 1490053578890
スレッドAおよびBの複数の到達時間は、CyclicBarrierをの使用は、同期の目的を達成するために同様に繰り返されます。
CyclicBarrierをしてたCountDownLatch可能混乱、私たちはその違いを強調する:
- たCountDownLatch参加スレッドは異なる役割、カウントダウンのためのいくつかを持って、カウントダウンスレッドのカウントダウンや待機する責任を0にカウントダウンのためのいくつかの待ち時間は、異なる役割間でスレッドを同期するために使用される複数のを持つことができます。
- CyclicBarrierを参加したスレッドの役割は、スレッド間の一貫性のために同じ役割、同じです。
- たCountDownLatchは使い捨てで、CyclicBarrierを再利用することができます。
概要
このセクションでは、Javaとの契約の同期コラボレーションツールのいくつかについて説明します。
- 使用ReentrantReadWriteLockは、パフォーマンスを向上させるために、代替ReentrantLockのどのように多くの書き込みシナリオを読んで
- 使用セマフォは、リソースへの同時アクセス数を制限します
- さまざまな役割のスレッド間で使用されたCountDownLatch同期
- 同じ役割のスレッド間で使用CyclicBarrierを調和
実際には、これらのツールは、待機/ロック/同期状態を通知したり表示して手動で好ま、代わりにする必要があります。
次のセクションでは、我々は特別なスレッドローカル変数のThreadLocalの概念を議論し、それは何ですか?