同步方法还是整个队列?

4
如果一个队列需要被多个线程访问,但目前只由单个方法getNextInQueue()进行修改,那么最适合同步访问队列的形式是什么?
目前,我将队列声明为ConcurrentLinkedQueue,但我不想出现多个线程等待锁释放而导致死锁的情况。另一种处理方式是不同步队列,而是同步getNextInQueue()。然而,随着这段代码在未来被使用,我认为这种方式无法很好地扩展。(每个修改队列的程序员都必须确保同步操作。)
您有什么想法?
3个回答

5

我认为最简单、最正确的方法是使用ConcurrentLinkedQueue。我不相信这会造成死锁,但是我不确定Concurrent包装器在使用迭代器时如何处理情况。我似乎记得必须回退到旧的synchronized方法来封装对基础集合(读写)的所有调用。我很确定Concurrent包装器正在做的就是这个。


2
从文档中可以得知:“迭代器是弱一致性的,返回反映队列状态的元素,该状态是从迭代器创建时或以后某个时间点处的状态。”所以如果您需要使用一致的快照,最好使用带有synchronizedLinkedList - millimoose
2
就我所知(通过查看后移植源代码),ConcurrentLinkedQueue不是任何东西的包装器。它是一个独立的集合,实现了不锁定所有数据。 - millimoose
@millimoose 你应该阅读链接的内容:“当对象的并发修改是不允许的时,可能会抛出此异常...请注意,无法保证快速失败行为...因此,编写一个依赖于此异常来确保正确性的程序是错误的:ConcurrentModificationException只应用于检测错误。” - Nir Alfasi
@alfasin,你能提供一个测试用例来展示ConcurrentLinkedQueue会做什么吗? - millimoose
@millimoose,你提到的是synchronized LinkedList而不是ConcurrentLinkedQueue。其次,就像手册所说的那样-你不能指望这种异常被抛出。行为是不可预测的。但是你问了一个有趣的问题,我没有尝试实现这样的异常。我明天会研究一下,现在太晚了;) - Nir Alfasi
显示剩余4条评论

3
如果是ConcurrentLinkedQueue,并且队列状态只是在线程之间共享数据,则不需要同步任何内容。这就是使用并发集合的全部意义。生产者-消费者设置不应该死锁,除非您做了一些奇怪的事情(例如将同一线程作为生产者和消费者)。

2
仅同步getNext()不是一个好主意,如果你想这样做,你也必须同步insert。例如:如果队列中没有元素,并且线程A尝试执行getNext()但未完成方法的执行,则线程B可能会将新项目插入队列中,这将导致线程A在队列上挂起,即使其中有新项目。
总之,如果并发性很重要,我建议使用ConcurrentLinkedQueue

-1:这并不适用于java.util.concurrent集合。 - millimoose
@millimoose 我在提到他关于“另一种处理方式是不同步队列”的想法。我认为你会同意,使用非同步队列并仅为 getter 实现同步可能会导致死锁。 - Nir Alfasi
嗯,我现在明白了,我猜那句话可以解释为“使用未同步的集合类型”(没有问题中的任何代码很难确定)。抱歉。 - millimoose

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