简体   繁体   English

Java中的并发和阻塞队列

[英]Concurrent and Blocking Queue in Java

I have the classic problem of a thread pushing events to the incoming queue of a second thread. 我有一个线程将事件推送到第二个线程的传入队列的经典问题。 Only this time, I am very interested about performance. 只有这一次,我对表现很感兴趣。 What I want to achieve is: 我想要实现的是:

  • I want concurrent access to the queue, the producer pushing, the receiver poping. 我希望并发访问队列,生产者推送,接收器弹出。
  • When the queue is empty, I want the consumer to block to the queue, waiting for the producer. 当队列为空时,我希望使用者阻塞队列,等待生产者。

My first idea was to use a LinkedBlockingQueue , but I soon realized that it is not concurrent and the performance suffered. 我的第一个想法是使用LinkedBlockingQueue ,但我很快就意识到它不是并发的并且性能受到了影响。 On the other hand, I now use a ConcurrentLinkedQueue , but still I am paying the cost of wait() / notify() on each publication. 另一方面,我现在使用ConcurrentLinkedQueue ,但我仍然在每个发布上支付wait() / notify()的成本。 Since the consumer, upon finding an empty queue, does not block, I have to synchronize and wait() on a lock. 由于消费者在找到空队列时不会阻塞,因此我必须同步并在锁上wait() On the other part, the producer has to get that lock and notify() upon every single publication. 另一方面,生产者必须获得该锁定并在每个单独的出版物上notify() The overall result is that I am paying the cost of sycnhronized (lock) {lock.notify()} in every single publication, even when not needed. 总体结果是我在每个出版物中支付sycnhronized (lock) {lock.notify()}的成本,即使不需要也是如此。

What I guess is needed here, is a queue that is both blocking and concurrent. 我猜这里需要的是一个阻塞和并发的队列。 I imagine a push() operation to work as in ConcurrentLinkedQueue , with an extra notify() to the object when the pushed element is the first in the list. 我想象一个push()操作在ConcurrentLinkedQueue工作,当push元素是列表中的第一个元素时,对象提供额外的notify() Such a check I consider to already exist in the ConcurrentLinkedQueue , as pushing requires connecting with the next element. 我认为这样的检查已经存在于ConcurrentLinkedQueue ,因为推送需要连接下一个元素。 Thus, this would be much faster than synchronizing every time on the external lock. 因此,这比每次在外部锁上同步要快得多。

Is something like this available/reasonable? 是这样的/合理的吗?

I think you can stick to java.util.concurrent.LinkedBlockingQueue regardless of your doubts. 我想你可以坚持使用java.util.concurrent.LinkedBlockingQueue而不管你的疑惑。 It is concurrent. 它是并发的。 Though, I have no idea about its performance. 虽然,我不知道它的表现。 Probably, other implementation of BlockingQueue will suit you better. 或许, BlockingQueue其他实现将更适合您。 There's not too many of them, so make performance tests and measure. 其中没有太多,所以进行性能测试和测量。

Similar to this answer https://stackoverflow.com/a/1212515/1102730 but a bit different.. I ended up using an ExecutorService . 类似于这个答案https://stackoverflow.com/a/1212515/1102730但有点不同..我最终使用了ExecutorService You can instantiate one by using Executors.newSingleThreadExecutor() . 您可以使用Executors.newSingleThreadExecutor()实例化一个。 I needed a concurrent queue for reading/writing BufferedImages to files, as well as atomicity with reads and writes. 我需要一个并发队列来读取/写入BufferedImages文件,以及读取和写入的原子性。 I only need a single thread because the file IO is orders of magnitude faster than the source, net IO. 我只需要一个线程,因为文件IO比源,网络IO快几个数量级。 Also, I was more concerned about atomicity of actions and correctness than performance, but this approach can also be done with multiple threads in the pool to speed things up. 此外,我更关注动作的原子性和正确性而不是性能,但是这种方法也可以通过池中的多个线程来完成,以加快速度。

To get an image (Try-Catch-Finally omitted): 获取图像(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();

To save an image (Try-Catch-Finally omitted): 保存图像(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();

It's important to note that you should flush and close your streams in a finally block. 重要的是要注意,您应该在finally块中刷新和关闭流。 I don't know about how it performs compared to other solutions, but it is pretty versatile. 与其他解决方案相比,我不知道它的表现如何,但它非常通用。

I would suggest you look at ThreadPoolExecutor newSingleThreadExecutor. 我建议你看一下ThreadPoolExecutor newSingleThreadExecutor。 It will handle keeping your tasks ordered for you, and if you submit Callables to your executor, you will be able to get the blocking behavior you are looking for as well. 它将处理为您订购的任务,如果您将Callables提交给执行者,您将能够获得您正在寻找的阻止行为。

You can try LinkedTransferQueue from jsr166: http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/ 您可以尝试jsr166中的LinkedTransferQueue: http ://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/

It fulfills your requirements and have less overhead for offer/poll operations. 它满足您的要求,并且提供/轮询操作的开销更少。 As I can see from the code, when the queue is not empty, it uses atomic operations for polling elements. 正如我从代码中看到的,当队列不为空时,它使用原子操作来轮询元素。 And when the queue is empty, it spins for some time and park the thread if unsuccessful. 当队列为空时,它会旋转一段时间并停止线程,如果不成功。 I think it can help in your case. 我认为它可以帮助你的情况。

I use the ArrayBlockingQueue whenever I need to pass data from one thread to another. 每当我需要将数据从一个线程传递到另一个线程时,我就使用ArrayBlockingQueue。 Using the put and take methods (which will block if full/empty). 使用put和take方法(如果满/空将阻止)。

Here is a list of classes implementing BlockingQueue . 以下是实现BlockingQueue的类列表

I would recommend checking out SynchronousQueue . 我建议查看SynchronousQueue

Like @Rorick mentioned in his comment, I believe all of those implementations are concurrent. 就像@Rorick在评论中提到的那样,我相信所有这些实现都是并发的。 I think your concerns with LinkedBlockingQueue may be out of place. 我认为您对LinkedBlockingQueue的担忧可能LinkedBlockingQueue

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM