web-dev-qa-db-ja.com

Java

スレッドが2番目のスレッドの着信キューにイベントをプッシュするという典型的な問題があります。今回だけ、パフォーマンスにとても興味があります。私が達成したいのは:

  • キューへの同時アクセス、プロデューサーによるプッシュ、レシーバーによるポップが必要です。
  • キューが空の場合、コンシューマーがキューにブロックしてプロデューサーを待機するようにします。

私の最初のアイデアはLinkedBlockingQueueを使用することでしたが、すぐにそれが同時ではなく、パフォーマンスが低下することにすぐに気付きました。一方、私はConcurrentLinkedQueueを使用していますが、各出版物についてwait()/notify()のコストを支払っています。コンシューマは、空のキューを見つけてもブロックされないため、同期してロックをwait()する必要があります。他の部分では、プロデューサーはそのロックを取得し、すべてのパブリケーションでnotify()を取得する必要があります。全体的な結果として、不要な場合でも、すべてのパブリケーションでsycnhronized (lock) {lock.notify()}のコストを支払っています。

ここで私が必要としているのは、ブロッキングと並行の両方のキューです。 Push()操作はConcurrentLinkedQueueと同じように機能し、プッシュされた要素がリストの最初にあるときにオブジェクトに対して追加のnotify()を使用すると想像できます。このようなチェックは、ConcurrentLinkedQueueにすでに存在すると見なします。プッシュを行うには、次の要素に接続する必要があるためです。したがって、これは外部ロックを毎回同期するよりもはるかに高速です。

このようなものは利用可能/合理的ですか?

27
Yiannis

疑いの余地なく、Java.util.concurrent.LinkedBlockingQueueに固執できると思います。同時です。でも、その性能はわかりません。おそらく、他のBlockingQueueの実装の方が適しています。それらはあまり多くないので、パフォーマンステストを行って測定します。

12
Rorick

この回答に似ています 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ブロックでストリームをフラッシュして閉じる必要があることに注意してください。他のソリューションと比較してどのように機能するかはわかりませんが、かなり用途が広いです。

6
ekangas

ThreadPoolExecutor newSingleThreadExecutorを参照することをお勧めします。それはあなたのためにあなたのタスクを順序付けられた状態に保つことを扱います、そしてあなたがエクゼキューターに Callables を提出するならば、あなたは同様にあなたが探しているブロッキングの振る舞いを得ることができます。

5
codethulhu

Jsr166からLinkedTransferQueueを試すことができます: http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/

要件を満たし、オファー/ポーリング操作のオーバーヘッドが少なくなります。コードからわかるように、キューが空でない場合は、要素のポーリングにアトミック操作を使用します。キューが空の場合、しばらくの間スピンし、失敗するとスレッドをパークします。それはあなたの場合に役立つと思います。

4
Vitaly

あるスレッドから別のスレッドにデータを渡す必要があるときはいつでも、ArrayBlockingQueueを使用します。 putおよびtakeメソッドを使用します(満杯または空の場合はブロックされます)。

3
Javamann

これが BlockingQueue を実装するクラスのリストです。

チェックアウト SynchronousQueue をお勧めします。

コメントで述べた@Rorickのように、これらの実装はすべて並行していると思います。 LinkedBlockingQueueに関するあなたの懸念は場違いかもしれません。

2
jjnguy