ConcurrentLinkedQueue的大小

17

阅读Java的ConcurrentLinkedQueue文档,我想知道为什么这个实现不可能存储大小:

请注意,与大多数集合不同,size方法不是一个常量时间操作。由于这些队列的异步性质,确定当前元素数量需要遍历元素。

源代码中,“异步性质”在哪里?我只看到一个while循环,重试入队,直到AtomicReferences匹配预期的值/引用。成功将一个值提供给队列后,为什么不能增加size:AtomicInteger

非常感谢。

3个回答

8
假设有两个线程,一个添加新项目,另一个删除项目。在开始时队列中没有任何项目。
假设第一个线程添加了该项目,紧接着另一个线程删除该项目并减少大小,此时您的大小为-1,然后第一个线程将大小增加到0。
这是一个略为牵强的例子,但您需要使整个操作原子化,以确保其他线程无法访问大小为-1的状态。

好的,我理解递减/递增的执行顺序是不确定的。但对于计数器来说,获取近似队列大小应该足够了,不是吗? - hotzen
1
@CPerkins:通过使用原子引用并且使用同步,Impl是无等待的。因此,您必须明确地在计数器上进行同步,因为增量/减量本身是原子性的,但offer/poll的整个操作不是原子性的,所以Finbarr应该是正确的。 - hotzen
@hotzen 加一赞,因为你的回答几乎和我要打的一模一样。 - Finbarr
这可能是一个很幼稚的问题,但如果并发集合被实现为异步的,那还有什么意义呢? - posdef
所描述的问题可以通过在添加项目之前递增计数器来轻松解决。一定还有其他原因。 - Gustav Karlsson
显示剩余3条评论

5
ConcurrentLinkedQueue的一个重要性能优势在于,当您更新头部时,不必担心尾部,反之亦然,对吧?
这基本上意味着如果队列大小不为0,那么2个线程可以同时进行poll/offer操作而不会互相干扰。
如果您有一个计数器,情况就不同了。即使它是具有良好并发性的AtomicInteger,您仍然可能会出现CAS操作失败的情况,因为现在您有一个“热点”,每次进行poll/offer操作时都要更新它。
我不确定作者是否是这个意思,但我认为这是他们不像您建议的那样使用计数器的最大原因。

有道理,谢谢。但是我真的想知道他们所说的“异步性质”是什么意思... - hotzen
3
为什么不去问作者呢?我相信他们会给你答案。只需要在Google上搜索“concurrency-interest”,然后在他们的邮件列表上发布即可。 - Enno Shioji

0
为什么在成功向队列中添加值后,无法对size:AtomicInteger进行递增操作?
可能是因为offer/decrement操作不能以原子方式执行,否则会对方法的并发性产生不利影响。

因为计数器将成为提供和轮询的同步点?这很有道理。然而,如果需要,我没有看到任何会妨碍这样一个计数器的“非同步”行为。 - hotzen

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