new SynchronousQueue()
new LinkedBlockingQueue(1)
違いはなんですか?キャパシティ1でいつ使用する必要がありますSynchronousQueue
か?LinkedBlockingQueue
new SynchronousQueue()
new LinkedBlockingQueue(1)
違いはなんですか?キャパシティ1でいつ使用する必要がありますSynchronousQueue
か?LinkedBlockingQueue
SynchronousQueueはハンドオフのようなものですが、LinkedBlockingQueueは単一の要素のみを許可します。違いは、SynchronousQueueへのput()呼び出しは、対応するtake()呼び出しがあるまで返されませんが、サイズ1のLinkedBlockingQueueでは、(空のキューへの)put()呼び出しはすぐに返されます。
SynchronousQueueを自分で直接使用したことがあるとは言えませんが、これはExecutors.newCachedThreadPool()
メソッドに使用されるデフォルトのBlockingQueueです。これは基本的に、キューが本当に必要ない場合(保留中のデータを維持したくない場合)のBlockingQueueの実装です。
私が理解している限り、上記のコードは同じことをします。
いいえ、コードはまったく同じではありません。
Sync.Q. オファーが成功するにはウェイターが必要です。LBQはアイテムを保持し、ウェイターがいなくてもオファーはすぐに終了します。
SyncQは、タスクのハンドオフに役立ちます。保留中のタスクを含むリストがあり、キューで待機している3つのスレッドがあるとしますoffer()
。受け入れられない場合は、リストの1/4を試して、スレッドが単独でタスクを実行できるようにします。[最後の1/4は、1/3ではなく1/4である理由がわからない場合は、現在のスレッドで処理する必要があります]
タスクをワーカーに渡そうと考えてください。利用できるものがない場合は、自分でタスクを実行する(または例外をスローする)オプションがあります。逆に、LBQの場合、タスクをキューに残しても実行が保証されるわけではありません。
注:コンシューマーとパブリッシャーの場合は同じです。つまり、パブリッシャーはコンシューマーをブロックして待機できますが、後offer
またはpoll
戻ると、タスク/要素が確実に処理されます。
SynchronousQueueを使用する理由の1つは、アプリケーションのパフォーマンスを向上させることです。スレッド間でハンドオフが必要な場合は、同期オブジェクトが必要になります。その使用に必要な条件を満たすことができる場合、SynchronousQueueは私が見つけた最速の同期オブジェクトです。他の人は同意します。参照:BlockingQueueの実装:SynchronousQueueとLinkedBlockingQueueの違いは何ですか
SynchronousQueueは同様の方法で機能しますが、次の大きな違いがあります。1)SynchronousQueueのサイズは0です。2)put()メソッドは、take()メソッドが同時にキューから要素をフェッチできる場合にのみ、要素を挿入します。コンシューマーのtake()呼び出しが要素を消費するのに時間がかかる場合、要素を挿入することはできません。
SynchronousQueue-誰かがその瞬間にそれを受け取るときにのみ挿入します。
[(おそらく)より明確な言葉でそれを入れようとしているだけです。]
SynchronousQueue
APIドキュメントには非常に明確に記載されていると思います。
- 各挿入操作が別のスレッドによる対応する削除操作を待機する必要があるブロッキングキュー、またはその逆。
- 同期キューには内部容量がなく、容量も1つではありません。要素は削除しようとしたときにのみ存在するため、同期キューを確認することはできません。別のスレッドが要素を削除しようとしない限り、(任意のメソッドを使用して)要素を挿入することはできません。繰り返すものがないため、繰り返すことはできません。
- キューの先頭は、キューに入れられた最初の挿入スレッドがキューに追加しようとしている要素です。そのようなキューに入れられたスレッドがない場合、削除できる要素はなく、
poll()
を返しnull
ます。
- 要素を取得するときにキューが空でなくなるのを待ち、要素を格納するときにキューでスペースが使用可能になるのを待つ操作を追加でサポートするキュー。
したがって、違いは明白で、やや批判的に微妙です。特に、以下の3番目のポイントは次のとおりです。
BlockingQueue
、新しい要素が挿入されるまで操作がブロックされます。また、に挿入するときにキューがいっぱいにBlockingQueue
なると、要素がキューから削除され、新しいキュー用のスペースが作成されるまで、操作はブロックされます。ただし、SynchronousQueue
では、操作がブロックされているため、別のスレッドで反対の操作(挿入と削除が互いに反対)が発生することに注意してください。したがって、とは異なりBlockingQueue
、ブロッキングは、要素の存在または非存在ではなく、操作の存在に依存します。peek()
常にを返しnull
(ここでも、APIドキュメントを確認してください)、常にを返すiterator()
空のイテレータを返します。(APIドキュメント)。ただし、このメソッドはこのキューの先頭を適切に取得して削除することに注意してください。別のスレッドが現在要素を使用可能にしている場合、そのようなスレッドが存在しない場合は、を返します。(APIドキュメント)hasNext()
false
poll()
null
最後に、小さなメモ、SynchronousQueue
とLinkedBlockingQueue
クラスの両方がBlockingQueue
インターフェイスを実装します。
同期キューは基本的にハンドオフの目的で使用されます。それらには容量がなく、他のスレッドがget操作を実行するまでput操作はブロックされます。
2つのスレッド間で変数を安全に共有したい場合は、その変数を同期キューに入れて、他のスレッドにキューから取得させることができます。
https://www.baeldung.com/java-synchronous-queueからのコードサンプル
ExecutorService executor = Executors.newFixedThreadPool(2);
SynchronousQueue<Integer> queue = new SynchronousQueue<>();
Runnable producer = () -> {
Integer producedElement = ThreadLocalRandom
.current()
.nextInt();
try {
queue.put(producedElement);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
};
Runnable consumer = () -> {
try {
Integer consumedElement = queue.take();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
};
executor.execute(producer);
executor.execute(consumer);
executor.awaitTermination(500, TimeUnit.MILLISECONDS);
executor.shutdown();
assertEquals(queue.size(), 0);
これらはCachedThreadPoolでも使用され、タスクの到着時に無制限(Integer.MAX)のスレッド作成の効果を実現します。CachedPoolのcoreSizeは0、maxPoolSizeはInteger.MAXで、同期キューがあります
タスクがキューに到着すると、最初のタスクがフェッチされるまで他のタスクはブロックされます。キュー容量がないため、スレッドプールは1つのスレッドを作成し、このスレッドはタスクを実行して、より多くのタスクをキューに配置できるようにします。これは、スレッドの作成がmaxPoolSizeに達するまで続きます。timeOutに基づいて、アイドル状態のスレッドが終了し、maxPoolSizeを超えずに新しいスレッドが作成される場合があります。