なぜ、生産者と消費者の間でキューによって分離されていますか?
まず、生産者と消費者の速度の不整合に起因するので、私たちは、バッファのためのスペースを必要としています。これは、長いデータがうまくバッファに投げとして、消費者の手に提出されたデータは、ジョブを必要としない生産者と消費者、生産者、生産データから切り離すことができます。これは、各実行できます。
なぜバッファキューはありますか?
一般的に、このバッファのデータ構造は、注文したキューです。順序が実際の要件に対処するためのものではありません場合は、実際には、キューを使用する必要はありません。補間は、することができます。
なぜバッファは、ロックを取得するためにアクセスされたとき?
このデータバッファ構造はマルチスレッド同時アクセス(生産者、消費者スレッド)であり、その構造を保護するために、一方の手をロックする必要があるコードの正しさを保証するために、他方では、破壊されません。
これは、使用してはならないのですか?
それは一緒に使用できますが、パフォーマンスが低下することがあります。
なぜ、パフォーマンスの低下?
このシナリオを考えてみましょう:バッファがいっぱいです。プロデューサーは、常にスロー物事を推進しようとしますので、我々は「ロックを獲得しました - ロックを解除 - ロックを取得 - 。ロックを解除」しています 一方で、生産者は、アイドリングCPUのタイムスライスを無駄に、それは他のスレッドのスケジューリングに影響します。消費者は、データの手で処理した場合は、この時点で、私はチャンスが契約を出て来てほしい、と今回は生産者が無用ロックをつかんだので、この時間という生産者と消費者は、不必要な競争になります。
これは、ああこれを行うことができますか?
シンプルでは、2つのケースが、一つは、バッファがいっぱいになったとき、生産者はそれを埋めるためにしようと、物事を投げ、それがハング入れています。同様に、消費者はそれを埋めるためにしようとした場合、バッファは、空であり、そしてそれがハングアップしたとき。
ときあなたはそれを覚ますだろうか?
(最初から)データへのバッファは、あなたが消費者のスレッドを覚ますとき、それは、あなたと同じではありません。バッファは(不満へのフルから)の空き容量がある場合、生産者スレッドを起動します。
このコードは、それを記述する方法ですか?
まず、単に「ロック」を実装するので、以下の通りです。
パブリック クラスロック{ / ** *待機しているスレッドのロックキュー * / プライベートリスト<スレッドが> = waitThreads 新しい新しいのArrayList <> (); / ** *ガード * / プライベートのAtomicIntegerガード=の新しい新しいのAtomicIntegerの(0 ); / ** *フラグをロック * / プライベートのAtomicInteger lockFlag =の新しい新規のAtomicIntegerの(0 ); / ** *現在のスレッドの所有者 * / プライベートスレッドホルダと 公共 ボイドロック(){ IF(Objects.equals(ホルダー、にThread.currentThread()))// スレッド・ロックは、直接のリターンが得られた場合には 、戻り、 しばらく(!Guard.compareAndSet(0、1))// ガードを取得しようと許され 、 IF(lockFlag.intValue()== 0 ){ lockFlag.set( 1) ; // ロックは「占有」とマークされた ホルダー=にThread.currentThreadを(); // ロック所有者は、現在のスレッドに設定されている guard.set(0); // 解放ガード } 他{ waitThreads.add(スレッド.currentThread()); // 待機キューに追加 guard.set(0); // 解放ガード LockSupport.park(); //現在のスレッドは中断されている ホルダーにThread.currentThread =(); // スレッドラインから実行を再開するとき、それはロック獲得するために、このスレッドを示す } } 公共 ボイドUNLOCK(){ IF(Objects.equals(ホルダー、スレッド! .currentThread()))// ないロック所有者は、何の資格がロック解除する場合は 返さない。 一方、(guard.compareAndSet(0、1] )) ; IF(waitThreads.size()== 0){ // かどうかを決定しますスレッドが待っている (0)lockFlag.setを; // ない場合は、それがマークされ、 "自由"にロックされます ホルダー= ヌル; guard.set( 0); // 解放ガード } 他{ LockSupport.unpark(waitThreads.remove( 0)); // スレッドがキューで待機している場合は最初のウェイクアップである guard.set(0); // 解放ガード } } }
その後、我々は、バッファの種類を実現するために来ます。
パブリック クラスbuffercache { / ** データを記憶するため*バッファアレイ * / プライベートオブジェクト[]データ; / ** *指数=読む>消費データを取る場合に、次の * / プライベート INTをreadIndex; / ** * =>着信データを入れて次の場所の書き込みインデックス * / プライベート int型; writeIndex / ** *バッファ内の現在のデータ数 * / プライベート int型のCOUNT; / ** *プロデューサーのスレッド待ち行列は、 * / プライベートリスト<スレッド> = waitProducers 新しい新しいのArrayList <> (); / ** *待ち行列の消費者のスレッド * / プライベート一覧<スレッド> = waitConsumers 新しい新しいのArrayList <> (); / ** *ロック以前に実装 * / プライベート・ロックロック= 新しい新しいロック(); 公共 buffercache(int型初期){ この .dataのは= 新しい新しいオブジェクト[イニシャル]; } 公共 のボイドPUT(オブジェクトE){ Lock.lock(); // ロックを取得 しばらく(COUNT == data.length){ // 完全な場合は、スレッドが生産を一時停止します waitProducers.add(にThread.currentThread()) ; // キューに、あなたは見つけることが目を覚ますことができ lock.unlockを(); //ロック解除 LockSupport.park(); //は、現在のスレッド中断 Lock.lockを(); //はロック取得に再び目が覚め } DATA [writeIndex] = Eを; // ロギングデータ COUNT ++ ; IF(++ == data.length writeIndex){ // リサイクルストレージ writeIndex = 0 ; } ながら(waitConsumers.size()= 0){!// ウェイクアップスレッド消費 LockSupport.unpark(waitConsumers.remove(0 )); } ロック.unlock(); // リリースロック } パブリックオブジェクトテイク(){// 同理 lock.lock(); オブジェクトE = nullを。 一方、(カウント== 0 ){ waitConsumers.add(にThread.currentThread())。 施錠開錠(); LockSupport.park(); lock.lock(); } E = データ[readIndex]。 カウント - ; もし(++ readIndex == data.length){ readIndex = 0 。 } 一方(waitProducers.size()!= 0 ){ LockSupport.unpark(waitProducers.remove(0 ))。 } lock.unlock()。 リターンE; } プライベート 静的 クラスタスク1が実装されたRunnable { // 生产任务 プライベート int型のnumは、 プライベートBufferCacheキャッシュ; プライベート文字列名; パブリックタスク1(BufferCacheキャッシュ、文字列インデックス){ この .nameのは= "producer-" + 指数; この .num = 0 ; この .cache = キャッシュ; } @Override 公共 のボイドRUN(){ 文字列データ、 しばらく(真の){ // 毎秒キューデータスローするように データを= NUM + + "から" 名; cache.put(データ); System.out.printlnは(名前 +「リリース:「+ データ); NUM ++ ; 試み{ のThread.sleep( 1000年); } キャッチ(InterruptedExceptionあるE){ e.printStackTrace(); } } } } プライベート 静的 クラス Task2のは、実装のRunnable { プライベートbuffercacheキャッシュ、 公共Task2の(buffercacheキャッシュ){ この .cache = キャッシュ; } @Override 公共 無効RUN(){ ながら、(真の){ // キューからデータをフェッチし続ける オブジェクトE = キャッシュ。テイク(); System.out.printlnは( "への消費:" + E); } } } 公共の 静的な 無効メイン(文字列[] args)を{ // 実行例 BufferCacheキャッシュ= 新しい BufferCache(20 )。 スレッドプロデューサー。 スレッドの消費者。 以下のために(int型、iは5 <; I = 0私は++){ // 开5个生产者 プロデューサー= 新しいスレッド(新しいタスク1(キャッシュが、私は"+" )); producer.start(); } のために(int型、iは3 <; I = 0 iは++){ // 开3个消费者 消費者は= 新しいスレッド(新しいタスク2(キャッシュ))。 consumer.start(); } } }
なぜ、条件付きの使用ループは、それを行う場合ではないながら?
プロデューサーのスレッドAのウェイク場合は、それが実行を再開することができたときに、バッファが再びプロデューサースレッドBで満たされているので、再び判断する必要があります。
なぜ、とき彼らはロックを取得するためのスレッドを復元するために行くだろうか?
ロックするには決意と実行条件が変更されない時に得られます。ようにコードを実装するのが正しいです。その後、詳細な点は、プロデューサーのスレッドがロックを取得する際に、他のスレッドは、(他の生産者スレッドは、変更したい場合ので、それは最初のロックを取得する必要があります)プロデューサーバッファの状態を変更することはできませんロック解除スレッドAを取得することであり、ロック時に、それは状態は変化しません見ています。
これら二つは、これは条件変数とは何かを持っているように、条件変数とキューように見えますか?
条件が満たされたとき、あなたは、1つまたは複数のスレッドを覚ますそれらを継続させることができ、実際には、これは、条件変数は、キューが条件を満たさない場合は、キューにスレッドを入れ、ある条件変数の本質です。
あなたは、JDK、ArrayBlockingQueueのBlockingQueueを実装するクラスを参照してください、そして、それは上記のコードを持つようなものであるかどうかを確認することができます。
関係ロックと条件変数
一方で、原因の条件変数にキューである、ときマルチスレッドアクセスは、そのスレッドセーフいることを確認する必要があり、それは通常、ロックオブジェクトに関連付けられています。このキューにアクセスするには、まずロックを取得する必要があります。
一方、条件判定は(決意と実行条件が変更されない時に保証)ロックと不可分である場合
だから、条件変数は、ロックに縛られ、ロックが分離または条件変数することはできません。条件を理解することは非常に簡単にロックオブジェクトによって生成されたオブジェクトJDKで、表示されます。
条件X = lock.newCondition()。