ArrayBlockingQueue:并发put和take

时间:2014-07-02 21:54:10

标签: java multithreading concurrency java.util.concurrent blockingqueue

为什么没有使用LinkedBlockingQueue的方式实现ABQ。我们可以使用AtomicInteger来保持ABQ中的跟踪计数,也与LBQ一样。我们也可以使用Two Locks for ABQ。 我偶然发现了关于SO的类似问题。 ArrayBlockingQueue uses a single lock for insertion and removal but LinkedBlockingQueue uses 2 separate locks

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

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

2 个答案:

答案 0 :(得分:2)

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个锁, AND 现有代码大致相同:

ArrayBlockingQueue已满

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

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

线程1:信号notFull()

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

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

答案 1 :(得分:0)

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