哪个Java阻塞队列对于单生产者单消费者场景最有效
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/976940/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Which Java blocking queue is most efficient for single-producer single-consumer scenarios
提问by Uri
I'm working on a standard Java system with critical timing requirements for my producers (1/100s of ms matters).
我正在开发一个标准 Java 系统,对我的制作人有关键的时间要求(1/100 毫秒很重要)。
I have a producer placing stuff in a blocking queue, and a single consumer later picking up that stuff and dumping it to a file. The consumer blocks when data is not available.
我有一个生产者把东西放在一个阻塞队列中,一个消费者后来拿起那个东西并将它转储到一个文件中。当数据不可用时,消费者会阻塞。
Obviously, blocking queue is the appropriate interface, but which actual implementation should I choose if I want to minimize the cost to the producer? I wan to play as little as possible on things like locking and allocating when I am putting stuff in the queue, and I don't mind if the consumer has to wait a lot longer or work a lot harder.
显然,阻塞队列是合适的接口,但如果我想最小化生产者的成本,我应该选择哪种实际实现?当我将东西放入队列时,我希望尽可能少地处理诸如锁定和分配之类的事情,而且我不介意消费者是否必须等待更长的时间或更努力地工作。
Is there an implementation that can be faster because I only have a single consumer and single producer ?
有没有可以更快的实现,因为我只有一个消费者和一个生产者?
采纳答案by Michael Myers
Well, there really aren't too many options. Let me go through the listed subclasses:
嗯,真的没有太多选择。让我来看看列出的子类:
DelayQueue
, LinkedBlockingDeque
, PriorityBlockingQueue
, and SynchronousQueue
are all made for special cases requiring extra functionality; they don't make sense in this scenario.
DelayQueue
, LinkedBlockingDeque
, PriorityBlockingQueue
, 和SynchronousQueue
都是为需要额外功能的特殊情况而设计的;在这种情况下,它们没有意义。
That leaves only ArrayBlockingQueue
and LinkedBlockingQueue
. If you know how to tell whether you need an ArrayList
or a LinkedList
, you can probably answer this one yourself.
那只剩下ArrayBlockingQueue
和LinkedBlockingQueue
。如果您知道如何判断您需要 anArrayList
还是 a LinkedList
,您可能可以自己回答这个问题。
Note that in LinkedBlockingQueue
, "linked nodes are dynamically created upon each insertion"; this might tend to push you toward ArrayBlockingQueue
.
请注意,在LinkedBlockingQueue
“每次插入时动态创建链接节点”;这可能会将您推向ArrayBlockingQueue
.
回答by Hank Gay
If the timing requirements are that tight, you'll probably need to do extensive benchmarking on exactly the same hardware to determine the best fit.
如果时间要求如此严格,您可能需要在完全相同的硬件上进行广泛的基准测试以确定最适合的硬件。
If I were to guess, I'd go with ArrayBlockingQueue
because array-based collections tend to have nice locality of reference.
如果我猜的话,我会去,ArrayBlockingQueue
因为基于数组的集合往往具有很好的引用位置。
回答by Andrew Duffy
LinkedBlockingQueue
will have O(1) insertion cost unless there is a memory allocation delay. A very large ArrayBlockingQueue
will have O(1) insertion cost unless the system is blocked due to a garbage collection; it will, however, block on insert when at capacity.
LinkedBlockingQueue
除非有内存分配延迟,否则将有 O(1) 插入成本。ArrayBlockingQueue
除非系统因垃圾收集而阻塞,否则非常大的插入成本将是 O(1);但是,它会在达到容量时在插入时阻塞。
Even with concurrent garbage collection I'm not sure if you should be writing a real-time system in a managed language.
即使使用并发垃圾收集,我也不确定您是否应该用托管语言编写实时系统。
回答by Victor
ArrayBlockingQueue will probably be faster as you do not need to create link nodes on insertion. Note, however, that ArrayBlockingQueue is of a fixed length, if you want your queue to grow arbitrarily large (at least to Integer.MAX_INT) you will have to use a LinkedBlockingQueue (unless you want to allocate an array of Integer.MAX_INT elements).
ArrayBlockingQueue 可能会更快,因为您不需要在插入时创建链接节点。但是请注意,ArrayBlockingQueue 是固定长度的,如果您希望您的队列增长到任意大(至少到 Integer.MAX_INT),您将不得不使用 LinkedBlockingQueue(除非您想分配一个 Integer.MAX_INT 元素的数组) .
回答by joshng
I agree with Andrew Duffy's remark that implementing such a time-constrained system in java may be a mistake. Regardless, assuming you're married to the JVM, and you don't expect this queue to be running full (ie, the consumer can handle the load), I think you might be best served by a custom implementation, similar to an ArrayBlockingQueue but trimmed down/optimized for the single producer/consumer scenario. In particular, I'm liking the notion of spinning on the producer-side to wait for space, rather than blocking.
我同意 Andrew Duffy 的评论,即在 Java 中实现这样一个时间受限的系统可能是一个错误。无论如何,假设您已使用 JVM,并且您不希望此队列已满运行(即,消费者可以处理负载),我认为您最好使用自定义实现,类似于 ArrayBlockingQueue但针对单个生产者/消费者场景进行了缩减/优化。特别是,我喜欢在生产者端旋转以等待空间而不是阻塞的概念。
I referred to the java.concurrent sourcesfor guidance, and drafted up this algorithm...
我参考了 java.concurrent源以获得指导,并起草了这个算法......
It looks pretty good to me, but it's entirely untested and may not be any faster in practice (probably not revolutionary, but I did crank it out myself :-). I had fun with it, anyway... Can you find a flaw?
它对我来说看起来不错,但它完全未经测试,在实践中可能不会更快(可能不是革命性的,但我确实自己制作了它:-)。反正我玩得很开心……你能找到缺陷吗?
Pseudocode:
伪代码:
private final ReentrantLock takeLock = new ReentrantLock();
private final Condition notEmpty = takeLock.newCondition();
private final E[] buffer;
private int head = 0
private int tail = 0;
public void put(E e) {
if (e == null) throw NullPointerException();
while (buffer[tail] != null) {
// spin, wait for space... hurry up, consumer!
// open Q: would a tight/empty loop be superior here?
Thread.sleep(1);
}
buffer[tail] = e;
tail = (tail + 1) % buffer.length;
if (count.getAndIncrement() == 0) {
sync(takeLock) { // this is pseudocode -- should be lock/try/finally/unlock
notEmpty.signal();
}
}
}
public E take() {
sync(takeLock) {
while (count.get() == 0) notEmpty.await();
}
E item = buffer[head];
buffer[head] = null;
count.decrement();
head = (head + 1) % buffer.length;
return item;
}
回答by Peter Lawrey
You can write a latency sensitive system in Java but you have to be careful of memory allocations, information passing between threads and locking. You should write some simple tests to see how much time these things cost on your server. (They vary bases on the OS and the memory architecture)
您可以用 Java 编写延迟敏感系统,但必须注意内存分配、线程之间的信息传递和锁定。您应该编写一些简单的测试来查看这些东西在您的服务器上花费了多少时间。(它们因操作系统和内存架构而异)
If you do this you can get stable timings for operations up to 99.99% of the time. This is not proper real-time, but it may be close enough. On a trading system, using Java instead of C costs might cost £100/d, however the cost of developing in C/C++ rather than Java is likely to be much higher than this. e.g. in terms of the flexibility it gives us and the number of bugs it saves.
如果您这样做,您可以获得高达 99.99% 的稳定运行时间。这不是正确的实时,但它可能足够接近。在交易系统上,使用 Java 而不是 C 的成本可能会花费 £100/d,但是使用 C/C++ 而不是 Java 开发的成本可能远高于此。例如,就它给我们的灵活性和它节省的错误数量而言。
You can get fairly close the same amount of jitter you would see in a C program doing the same thing. Some call this "C-like" Java.
您可以获得与在执行相同操作的 C 程序中看到的相同数量的抖动相当接近。有人称其为“类 C”Java。
Unfortunately it takes about 8 micro-second to pass an object between threads in Java via a ArrayBlockingQueue on the servers I work on and I suggest you test this on your servers. A simple test is to pass the System.nanoTime() between threads and see how long it takes.
不幸的是,通过我工作的服务器上的 ArrayBlockingQueue 在 Java 线程之间传递对象大约需要 8 微秒,我建议您在您的服务器上进行测试。一个简单的测试是在线程之间传递 System.nanoTime() 并查看需要多长时间。
ArrayBlockingQueue has a "feature" where it creates an object on each element added (though there is not much of a good reason to do so) So if you find a standard implementation which doesn't do this, let me know.
ArrayBlockingQueue 有一个“功能”,它在添加的每个元素上创建一个对象(尽管没有太多充分的理由这样做)因此,如果您发现标准实现不这样做,请告诉我。
回答by dvvrt
You can look into using LinkedTransferQueue, which implements "Dual Queue with Slack" and is good at matching Producers and Consumers. See following for more details Java 7 TransferQueue, Dual Synchronous Queues (.pdf)and LinkedTransferQueue source
您可以考虑使用LinkedTransferQueue,它实现了“带 Slack 的双队列”并且擅长匹配生产者和消费者。有关更多详细信息,请参阅以下 Java 7 TransferQueue、Dual Synchronous Queues (.pdf)和 LinkedTransferQueue源