ArrayBlockingQueue:并发put和take

为什么没有使用LinkedBlockingQueue的方式实现ABQ。 我们可以使用AtomicInteger来保持ABQ中的跟踪计数,也与LBQ一样。 我们也可以使用Two Locks for ABQ。 我偶然发现了关于SO的类似问题。 ArrayBlockingQueue使用单个锁进行插入和删除,但LinkedBlockingQueue使用2个单独的锁

但我无法理解这个问题的答案。 我需要帮助来理解如果我们使用两个锁实现ABQ会出现的问题。 如果有人可以举一个可能失败的竞争条件的例子,那将是非常好的。 这个问题可以标记为重复,但我真的在寻找更具描述性的答案。 那将是一个很大的帮助。

我在这里贴了一个代码http://pastebin.com/ZD1uFy7S 。 任何人都可以显示粘贴的代码中是否存在可能的竞争条件。

根据定义,ArrayBlockingQueue在发生put()并且其固定数组已满时阻止等待空间变为可用

可用空间是take()返回的元素。 换句话说,固定数组中的元素随着时间的推移而被重用。 put() 必须将其项目写入数组中的特定位置

另一方面,LinkedBlockingQueue是一个链表。 对于这个讨论,假设你创建了一个有界的,只是为了使它更像ArrayBlockingQueue。 尝试同样的事情:

当LinkedBlockingQueue已满时put()一个元素。 它将等待元素变为可用。

但在这种情况下,当你执行一个take()时,它只会返回头部值并核实该项目。 然后put()看到LinkedBlockingQueue低于容量。 它将其项目链接到列表的尾部。 没有像ArrayBlockingQueue那样覆盖内存,它必须保持连续。

编辑:这是一种假设的练习,因为代码不是这样编写的。 但无论如何,这里有更多细节,特别是插入和提取方法: http : //grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/concurrent/ArrayBlockingQueue的.java

使用了IF 2锁定的潜在问题,并且现有代码大致相同:

ArrayBlockingQueue已满

线程1:调用take(),获取锁定A,执行其操作,减少计数到[capacity-1] – 但尚未完成

线程2:调用put(),在T1仍在运行时获取锁定B,将计数增加到[capacity],释放锁定B.

线程1:信号notFull()

线程3:put()开始执行,即使数组确实已满,也会覆盖一个元素,因为ArrayBlockingQueue使用循环增量。

在LinkedBlockingQueue中不会发生这种情况。

我研究了一下。 我得出的结论是,ABQ可以像LBQ一样实现。 我在粘贴箱中粘贴了一个代码来显示http://pastebin.com/ZD1uFy7S 。 不实现这种方式的主要原因是因为代码变得复杂,特别是因为支持迭代器并且性能优势对于更复杂的实现并不重要。