简体   繁体   English

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

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

I have a producer consumer like pattern where some threads are creating data and periodically passing putting chunks of that data to be consumed by some other threads. 我有一个生产者消费者喜欢模式,其中一些线程正在创建数据并定期传递放置的数据块以供其他一些线程使用。

Keeping the Java Memory Model in mind, how do i ensure that the data passed to the consumer thread has full 'visibility'? 牢记Java内存模型,我如何确保传递给消费者线程的数据具有完全“可见性”?

I know there are data structures in java.util.concurrent like ConcurrentLinkedQueue that are built specifically for this, but I want to do this as low level as possible without utilizing those and have full transparency on what is going on under the covers to ensure the memory visibility part. 我知道java.util.concurrent中的数据结构就像ConcurrentLinkedQueue那样是专门为此而构建的,但是我希望尽可能低地使用这些数据结构而不使用它们,并且对封底下的内容有完全的透明度以确保记忆可视性部分。

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

To transfer data, you need a field somewhere available to all threads. 要传输数据,您需要一个可供所有线程使用的字段。 In your case it really needs to be some sort of collection to handle multiple entries. 在你的情况下,它确实需要某种集合来处理多个条目。 If you made the field final , referencing, say, a ConcurrentLinkedQueue, you'd pretty much be done. 如果你让这个领域成为final ,比如引用ConcurrentLinkedQueue,你就会完成任务。 The field could be made public and everyone could see it, or you could make it available with a getter. 该领域可以公开,每个人都可以看到它,或者你可以用吸气剂使它可用。

If you use an unsynchronized queue, you have more work to do, because you have to manually synchronize all access to it, which means you have to track down all usages; 如果使用非同步队列,则还有更多工作要做,因为您必须手动同步对它的所有访问,这意味着您必须跟踪所有用法; not easy when there's a getter method. 当有一个getter方法时不容易。 Not only do you need to protect the queue from simultaneous access, you must make sure interdependent calls end up in the same synchronized block. 您不仅需要保护队列不被同时访问,还必须确保相互依赖的调用最终位于同一个同步块中。 For instance: 例如:

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

If the whole thing is not synchronized, queue is perfectly capable of telling you it is not empty, then throwing a NoSuchElementException when you try to get the next element. 如果整个事情没有同步,那么queue完全能够告诉你它不是空的,然后当你试图获取下一个元素时抛出NoSuchElementException。 (ConcurrentLinkedQueue's interface is specifically designed to let you do operations like this with one method call. Take a good look at it even if you don't want to use it.) (ConcurrentLinkedQueue的界面专门设计用于通过一个方法调用来执行此类操作。即使您不想使用它,也要仔细查看它。)

The simple solution is to wrap the queue in another object whose methods are carefully chosen and all synchronized. 简单的解决方案是将队列包装在另一个对象中,该对象的方法经过精心选择并且全部同步。 The wrapped class, even if it's LinkedList or ArrayList, will now act (if you do it right) like CLQ, and it can be freely released to the rest of the program. 包装类,即使它是LinkedList或ArrayList,现在将像CLQ一样行动(如果你做对了),它可以自由地释放到程序的其余部分。

So you would have what is really a global field with an immutable ( final ) reference to a wrapper class, which contains a LinkedList (for example) and has synchronized methods that use the LinkedList to store and access data. 因此,您将拥有一个真正的全局字段,其中包含对包含LinkedList(例如)的包装类的不可变( final )引用,并且具有使用LinkedList存储和访问数据的同步方法。 The wrapper class, like CLQ, would be thread-safe. 包装类(如CLQ)将是线程安全的。

Some variants on this might be desirable. 有些变体可能是可取的。 It might make sense to combine the wrapper with some other high-level class in your program. 将包装器与程序中的其他高级类组合可能是有意义的。 It might also make sense to create and make available instances of nested classes: perhaps one that only adds to the queue and one that only removes from it. 创建和创建嵌套类的实例也可能是有意义的:可能只添加到队列中的一个和只从中删除的一个。 (You couldn't do this with CLQ.) (你无法用CLQ做到这一点。)

A final note: having synchronized everything, the next step is to figure out how to unsynchronize (to keep threads from waiting too much) without breaking thread safety. 最后一点:已经同步了所有内容,下一步是弄清楚如何在不破坏线程安全的情况下进行不同步(以防止线程等待太多)。 Work really hard on this , and you'll end up rewriting ConcurrentLinkedQueue. 这方面工作非常努力,你最终将重写ConcurrentLinkedQueue。

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

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