Javaの同時およびブロッキングキュー
-
06-07-2019 - |
質問
2番目のスレッドの着信キューにイベントをプッシュするスレッドの古典的な問題があります。今回だけ、パフォーマンスにとても興味があります。私が達成したいのは:
- キューへの同時アクセス、プロデューサーのプッシュ、レシーバーのポップが必要です。
- キューが空の場合、コンシューマがキューをブロックし、プロデューサを待機するようにします。
最初のアイデアは LinkedBlockingQueue
を使用することでしたが、すぐにそれが同時ではなく、パフォーマンスが低下することがわかりました。一方、今では ConcurrentLinkedQueue
を使用していますが、それでも各パブリケーションで wait()
/ notify()
のコストを支払っています。コンシューマーは空のキューを見つけるとブロックされないため、ロックを同期して wait()
する必要があります。一方、プロデューサーはパブリケーションごとにそのロックと notify()
を取得する必要があります。全体的な結果は、私がコストを払っているということです
sycnhronized(lock){lock.notify()}
すべてのパブリケーションで、不要な場合でも。
ここで必要なのは、ブロッキングとコンカレントの両方のキューです。 ConcurrentLinkedQueue
のように動作する push()
操作を想像します。プッシュされた要素が最初の場合、オブジェクトに追加の notify()
を追加しますリスト内。プッシュは次の要素との接続を必要とするため、このようなチェックは ConcurrentLinkedQueue
にすでに存在すると考えています。したがって、外部ロックを毎回同期するよりもはるかに高速です。
このようなものは利用可能/合理的ですか?
解決
疑問に関係なく java.util.concurrent.LinkedBlockingQueue
に固執できると思います。同時発生です。ただし、そのパフォーマンスについてはわかりません。おそらく、 BlockingQueue
の他の実装がより適しています。あまり多くないので、パフォーマンステストと測定を行います。
他のヒント
この回答 https://stackoverflow.com/a/1212515/1102730 に似ていますが、少し異なります。最終的に ExecutorService
を使用しました。 Executors.newSingleThreadExecutor()
を使用してインスタンス化できます。 BufferedImagesをファイルに読み書きするための並行キューと、読み書きのアトミック性が必要でした。ファイルIOはソースのネットIOよりも桁違いに速いため、必要なのは単一のスレッドだけです。また、パフォーマンスよりもアクションのアトミック性と正確性に関心がありましたが、このアプローチはプール内の複数のスレッドを使用して実行することもできます。
画像を取得するには(最後にTry-Catch-Finalally省略):
Future<BufferedImage> futureImage = executorService.submit(new Callable<BufferedImage>() {
@Override
public BufferedImage call() throws Exception {
ImageInputStream is = new FileImageInputStream(file);
return ImageIO.read(is);
}
})
image = futureImage.get();
画像を保存するには(最後にTry-Catch-Finalally省略):
Future<Boolean> futureWrite = executorService.submit(new Callable<Boolean>() {
@Override
public Boolean call() {
FileOutputStream os = new FileOutputStream(file);
return ImageIO.write(image, getFileFormat(), os);
}
});
boolean wasWritten = futureWrite.get();
finallyブロックでストリームをフラッシュして閉じる必要があることに注意することが重要です。他のソリューションと比較した場合のパフォーマンスについてはわかりませんが、非常に多用途です。
ThreadPoolExecutor newSingleThreadExecutor。タスクの順序を維持し、 Callables をエグゼキューターに追加すると、探しているブロック動作も取得できます。
jsr166からLinkedTransferQueueを試すことができます: http ://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/
これは要件を満たし、オファー/ポーリング操作のオーバーヘッドが少なくなります。 コードからわかるように、キューが空でない場合、要素のポーリングにアトミック操作を使用します。キューが空になると、しばらくスピンし、失敗した場合はスレッドをパークします。 あなたの場合に役立つと思います。
1つのスレッドから別のスレッドにデータを渡す必要がある場合は常に、ArrayBlockingQueueを使用します。 putメソッドとtakeメソッドを使用する(フル/空の場合はブロックされます)。
BlockingQueue
を実装するクラスのリスト。
@Rorickがコメントで言及したように、これらの実装はすべて並行していると思います。 LinkedBlockingQueue
に関するあなたの懸念は、場違いかもしれません。