0

我有一个生产者消费者喜欢模式,其中一些线程正在创建数据并定期传递该数据块以供其他线程使用。如何在跨线程传递数据时确保Java内存可见性

记住Java内存模型,我如何确保传递给消费者线程的数据具有完整的“可见性”?

我知道java.util.concurrent中的数据结构像ConcurrentLinkedQueue是专门为此构建的,但我想尽可能低级别地执行此操作,而不利用这些数据结构,并且对于在涵盖以确保内存可见性的一部分。

+0

*“关于在封面下发生了什么以确保内存可见性部分完全透明。”* - 你不相信'ConcurrentLinkedQueue'吗? –

+1

作为一个附注,ConcurrentLinkedQueue对生产者/消费者来说并不好,因为它没有阻塞设施。通常,BlockingQueue实现对于生产者/消费者来说是理想的。 – jtahlborn

回答

1

如果你想要“低级”,然后看看volatile和​​。

1

要传输数据,您需要一个可供所有线程使用的字段。在你的情况下,它确实需要某种集合来处理多个条目。如果你创建了final这个字段,比如引用一个ConcurrentLinkedQueue,那么你几乎可以完成。该领域可以公开,每个人都可以看到它,或者你可以让它与吸气剂一起使用。

如果您使用非同步队列,则需要做更多工作,因为您必须手动同步对它的所有访问,这意味着您必须跟踪所有用法;有一个getter方法时不容易。您不仅需要保护队列不受同时访问,还必须确保相互依赖的调用最终位于同一个同步块中。例如:

if (!queue.isEmpty()) obj = queue.remove(); 

如果整个事情是不同步的,queue完全能够告诉你它不是空的,然后抛出一个NoSuchElementException当您尝试获得下一个元素。 (ConcurrentLinkedQueue的界面是专门设计用来让你用一个方法调用这样的操作,即使你不想使用它,也要仔细看看它。)

简单的解决方案是将队列包装在另一个对象的方法都经过精心挑选全部同步。包装的类,即使它是LinkedList或ArrayList,现在也会像CLQ一样行事(如果你做得对),它可以自由地发布到​​程序的其余部分。

所以你必须什么是真正的与不变(final)参照包装类,其中包含一个LinkedList(例如),并同步使用LinkedList的存储和访问数据的方法一个全球性的领域。包装类,如CLQ,将是线程安全的。

这可能需要一些变体。将包装器与程序中的其他高级类组合起来可能很有意义。创建和提供嵌套类的实例也许是有意义的:可能只有一个添加到队列中,而一个只能从队列中删除。 (您无法CLQ做到这一点。)

最后请注意:具有同步的一切,下一步就是要弄清楚如何取消同步(保持线程等待太多)不破坏线程安全。在这个上真的很努力,你最终会重写ConcurrentLinkedQueue。