スレッドが2番目のスレッドの着信キューにイベントをプッシュするという典型的な問題があります。今回だけ、パフォーマンスにとても興味があります。私が達成したいのは:
私の最初のアイデアはLinkedBlockingQueue
を使用することでしたが、すぐにそれが同時ではなく、パフォーマンスが低下することにすぐに気付きました。一方、私はConcurrentLinkedQueue
を使用していますが、各出版物についてwait()
/notify()
のコストを支払っています。コンシューマは、空のキューを見つけてもブロックされないため、同期してロックをwait()
する必要があります。他の部分では、プロデューサーはそのロックを取得し、すべてのパブリケーションでnotify()
を取得する必要があります。全体的な結果として、不要な場合でも、すべてのパブリケーションでsycnhronized (lock) {lock.notify()}
のコストを支払っています。
ここで私が必要としているのは、ブロッキングと並行の両方のキューです。 Push()
操作はConcurrentLinkedQueue
と同じように機能し、プッシュされた要素がリストの最初にあるときにオブジェクトに対して追加のnotify()
を使用すると想像できます。このようなチェックは、ConcurrentLinkedQueue
にすでに存在すると見なします。プッシュを行うには、次の要素に接続する必要があるためです。したがって、これは外部ロックを毎回同期するよりもはるかに高速です。
このようなものは利用可能/合理的ですか?
疑いの余地なく、Java.util.concurrent.LinkedBlockingQueue
に固執できると思います。同時です。でも、その性能はわかりません。おそらく、他のBlockingQueue
の実装の方が適しています。それらはあまり多くないので、パフォーマンステストを行って測定します。
この回答に似ています https://stackoverflow.com/a/1212515/11027 が少し異なります。ExecutorService
を使用してしまいました。 Executors.newSingleThreadExecutor()
を使用してインスタンス化できます。 BufferedImagesをファイルに読み書きするための同時キュー、および読み取りと書き込みの原子性が必要でした。ファイルIOはソースのネットIOよりも桁違いに速いため、単一のスレッドのみが必要です。また、パフォーマンスよりもアクションのアトミック性と正確性に関心がありましたが、このアプローチではまた、プール内の複数のスレッドを使用して処理を高速化します。
画像を取得するには(Try-Catch-Finally省略):
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-Finally省略):
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/
要件を満たし、オファー/ポーリング操作のオーバーヘッドが少なくなります。コードからわかるように、キューが空でない場合は、要素のポーリングにアトミック操作を使用します。キューが空の場合、しばらくの間スピンし、失敗するとスレッドをパークします。それはあなたの場合に役立つと思います。
あるスレッドから別のスレッドにデータを渡す必要があるときはいつでも、ArrayBlockingQueueを使用します。 putおよびtakeメソッドを使用します(満杯または空の場合はブロックされます)。
これが BlockingQueue
を実装するクラスのリストです。
チェックアウト SynchronousQueue
をお勧めします。
コメントで述べた@Rorickのように、これらの実装はすべて並行していると思います。 LinkedBlockingQueue
に関するあなたの懸念は場違いかもしれません。