ArrayBlockingQueue使用一个锁来进行插入和删除,而LinkedBlockingQueue使用2个单独的锁。

11
我正在查看ArrayBlockingQueue和LinkedBlockingQueue的源代码。LinkedBlockingQueue针对插入和删除分别使用putLock和takeLock,但是ArrayBlockingQueue只使用一个锁。我认为LinkedBlockingQueue是基于Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms中描述的设计实现的。在这篇论文中,他们提到保留虚拟节点,以便enqueuers永远不必访问head,dequeuers永远不必访问tail,从而避免死锁情况。我想知道为什么ArrayBlockingQueue不借鉴同样的想法,使用2个锁。

这个与问题相关的更详细的答案可以在这里找到[链接](https://dev59.com/R3bZa4cB1Zd3GeqPDDWJ)。 - amarnath harish
4个回答

13
我想知道为什么ArrayBlockingQueue不借用同样的思路,使用2个锁呢?
因为ArrayBlockingQueue使用一个更简单的数据结构来保存队列项。
ArrayBlockingQueue将其数据存储在一个私有的final E[] items数组中。为了让多个线程处理相同的存储空间,无论是添加还是出队,它们都必须使用相同的锁。这不仅是因为内存屏障,而且因为互斥保护,因为它们正在修改同一个数组。
另一方面,LinkedBlockingQueue是一个完全不同的队列元素链表,允许具有双重锁定功能。正是队列中元素的内部存储使得不同的锁定配置成为可能。

12

ArrayBlockingQueue必须避免覆盖条目,因此需要知道起始位置和结束位置。LinkedBlockQueue不需要了解这一点,因为它让GC担心清理队列中的节点。


谢谢您指引我正确的方向。我再次检查了代码,发现ArrayBlockingQueue和LinkedBlockingQueue都维护变量“count”。在LinkedBlockingQueue中,这是AtomicInteger(在put和remove中相应地递增和递减),而在ArrayBlockingQueue中则是int。我猜在ArrayBlockingQueue中它不能是AtomicInteger,因为它必须环绕。 - user1168577
@Peter,如果您能解释为什么“双锁队列算法”不起作用,那将非常感激。在ABQ的情况下,计数可以很好地使用AtomicInteger,并且通过检查count == max_capacity可以很好地避免覆盖。 - veritas
1
@veritas,你能解释一下我在哪里提到了“双锁队列算法”吗?我是两年前写的,现在找不到你在说什么。 - Peter Lawrey
2
LBQ实现中使用了“双锁队列算法”。LBQ的取和放可以并发进行,但ABQ不行。所以想知道为什么?这是最初的问题。但是无法理解答案。 - veritas
@PeterLawrey,我已经贴出了使用LBQ方法的ABQ的小实现。你能指出其中的竞态条件吗?http://pastebin.com/ZD1uFy7S。谢谢。 - veritas

1

在LBQ中,使用2个锁来限制对头部的访问和同时锁定。头部锁定禁止同时删除两个元素,尾部锁定禁止同时添加两个元素到队列中。这两个锁一起防止竞争。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接