繁体   English   中英

如何在跨线程传递数据时确保Java中的内存可见性

[英]How to Ensure Memory Visibility in Java when passing data across threads

我有一个生产者消费者喜欢模式,其中一些线程正在创建数据并定期传递放置的数据块以供其他一些线程使用。

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

我知道java.util.concurrent中的数据结构就像ConcurrentLinkedQueue那样是专门为此而构建的,但是我希望尽可能低地使用这些数据结构而不使用它们,并且对封底下的内容有完全的透明度以确保记忆可视性部分。

如果你想要“低级”,那么看看volatilesynchronized

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

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

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

如果整个事情没有同步,那么queue完全能够告诉你它不是空的,然后当你试图获取下一个元素时抛出NoSuchElementException。 (ConcurrentLinkedQueue的界面专门设计用于通过一个方法调用来执行此类操作。即使您不想使用它,也要仔细查看它。)

简单的解决方案是将队列包装在另一个对象中,该对象的方法经过精心选择并且全部同步。 包装类,即使它是LinkedList或ArrayList,现在将像CLQ一样行动(如果你做对了),它可以自由地释放到程序的其余部分。

因此,您将拥有一个真正的全局字段,其中包含对包含LinkedList(例如)的包装类的不可变( final )引用,并且具有使用LinkedList存储和访问数据的同步方法。 包装类(如CLQ)将是线程安全的。

有些变体可能是可取的。 将包装器与程序中的其他高级类组合可能是有意义的。 创建和创建嵌套类的实例也可能是有意义的:可能只添加到队列中的一个和只从中删除的一个。 (你无法用CLQ做到这一点。)

最后一点:已经同步了所有内容,下一步是弄清楚如何在不破坏线程安全的情况下进行不同步(以防止线程等待太多)。 这方面工作非常努力,你最终将重写ConcurrentLinkedQueue。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM