问题 SynchronousQueue与Exchanger


什么是Exchanger和SynchronousQueue之间的区别?以及可以使用它们的场景?哪一个表现更好? (明智的锁?)


11931
2018-03-16 10:35


起源

除非微秒计数,否则使用哪一个并不重要。 - Peter Lawrey


答案:


一个 Exchanger 更多的是纯粹的同步机制而a SynchronousQueue 另外提供标准队列数据结构的所有操作。这意味着您可以通过异步删除队列中的项目等来检查队列中的对象,取消已调度但尚未执行的操作等。 Exchanger 不提供。由于许多实现允许对队列大小设置限制,因此您可以获得对资源使用的额外控制,并且如果队列增长超过某个阈值,则可以删除请求。另一方面, Exchanger 提供开箱即用的双向通信,而单个队列只是一种方式(尽管可以手动实现另一方向的通信)。由于许多实际情况只需要生产者 - 消费者关系,因此队列通常更好,因为上面列出的API和其他操作更容易理解。

本文 描述了一个实际的用例 Exchanger。他们专注于在线程之间进行通信时能够避免创建和垃圾收集新对象(取决于实现,队列可能在您向其追加内容时分配条目)。性能可能取决于您的特定用例。在他们使用的示例中 Exchanger 为了提高效率(避免垃圾收集),但在大多数情况下(如果你不必提供亚毫秒延迟),分配一个或两个对象并不是一个大问题,我更喜欢使用队列进行额外的控制它允许。

编辑: 我检查了源代码 Exchanger.java 在Oracle JDK中,它确实创建了类的临时对象 Exchanger.Node 在 Exchanger.doExchange()。因此,似乎与链接文章的作者所说的相反, Exchanger 不是免分配的。两者都没有(相当明显) LinkedBlockingQueue。该 ArrayBlockingQueue相反,当项目附加到它时,不会分配任何临时对象。它只分配一个数组来保存创建时允许的最大元素数,但这只是一次性操作。在使用过程中,它不会创建新对象,因此从纯GC的角度来看它应该更好 Exchanger


9
2018-03-16 10:58



谢谢Michal。您能否详细说明Exchanger如何避免创建和垃圾收集新对象?是否队列为新节点分配/解除分配对象,而交换器只是帮助交换引用? - Hemanshu
Exchanger,在两个线程之间交换一个对象。这意味着您可以有两个来回传递的数据结构,可以一次又一次地重复使用。如果这是一个大型或昂贵的物体,例如一个ByteBuffer,这可能会使SynchronousQueue变得不切实际。 - Peter Lawrey
@Hemanshu队列可能需要包装器,例如a LinkedBlockingQueue 需求 Node 保存其有效负载的对象以及对下一个链接项的引用。一个 ArrayBlockingQueue 不需要这样的包装,但与之形成对比 LinkedBlockingQueue 它必须事先为其最大大小分配一个数组。这是一次性操作,所以通常没有问题,但是,这是一个分配。因此,队列可以(但不是必须)导致每个项目的额外分配。现在,我检查了代码 Exchanger 它确实创造了临时性 Node 对象。这篇文章的作者似乎是完全错误的。 - Michał Kosmulski
@PeterLawrey队列不克隆存储在其中的对象,它只是传递引用,因此它也可以用于在线程之间传递相同的对象。 Exchanger 但是,它具有双向优势,所以我猜这就是利润所在。但两个队列和 Exchanger 由于内部工作原因,创建一些临时对象。 - Michał Kosmulski
@PeterLawrey我检查了源代码 Exchanger.java 它确实创建了类的临时对象 Exchanger.Node 在 Exchanger.doExchange() - Michał Kosmulski


让我们关注关键点.. Exchanger:是两个线程可以交换对象的集合点 SynchQueue:是队列!一个线程放置并等待,直到另一个线程弹出 Exchanger是一种双向SyncQueue


2
2017-07-17 00:05