易失性变量和内存屏障 [英] volatile variables and memory barrier in java

查看:181
本文介绍了易失性变量和内存屏障的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个由链接节点组成的数据结构。你可以把它想象成一个简单的LinkedList。列表的每个节点由一些值和指向另一个节点的下一个字段组成,如果它是最后一个节点,则为null。第一个节点作为根,它没有值,它只指向下一个节点。所有其他节点实际上是不可变的,一旦它们在它们的生命周期中不创建它们的值或它们的下一个字段改变,除非与特定情况相关的结构被处置。



一个(只有一个)线程将新节点添加到列表的前面。它是通过构造一个新对象,设置其字段并将下一个字段设置为根指向的对象,然后将根的下一个字段设置为这个新节点来实现的。



其他节点浏览结构只执行读。他们有一个对根节点的引用,然后他们通过其他节点,直到他们找到正在寻找或到达列表的末尾。



我的问题是:是否足以使下一个字段不稳定?从我对java内存模型的理解,如果主线程(添加新节点的线程)将在添加一个新节点时执行一个易失性写操作,那么一切都将同步,并且不会出现不一致。



也可以假设在x86架构上,volatile变量的读取不会导致任何性能下降?由于其他线程会频繁浏览结构读取下一个字段,这是很重要的,这可以自由地没有任何内存障碍等。



我还有一个关心。要浏览结构的线程也将持有一些额外的节点。这些节点将是完全线程本地的,它们将只被创建它们的线程使用,并且不会被共享。
对于这些附加节点,下一个字段是不必要的。此外,设置易失性下一个字段将发出存储器屏障,这将导致不期望的性能损失。
我想知道是否有办法避免这种情况。理想情况下,如果下一个字段有时作为一个挥发性字段工作,有时候作为一个正常字段;)或者如果我有一个完全的控制,并可以发布内存障碍,我自己,每当我需要时,只是完美。



编辑



我也想知道是否可能以不同的方式同步所有这些写入易变变量?例如一些其他完全不相关的静态变量?因为volatile写入刷新所有挂起的写入,下一个字段是否不可能是volatile,而是在更新线程完成所有的工作后写入一个不同的volatile变量?



它对我来说看起来不安全,因为在关系之前没有发生,以前的写入可能会重新排序。下一个字段赋值可以通过值字段赋值导致迭代线程观察到不一致的对象状态。



但是也许可以想出一个方案安全?如何这一个:



更新线程首先构造一个新对象,初始化其值字段,将其下一个字段设置为根节点指向的节点,对某些静态变量写入非易失性写入,将根节点的下一个字段设置为新创建的节点

解决方案

1



根据您在此处说的内容


其字段和
将下一个字段设置为由根指向的对象
,然后将
root的下一个字段设置为此新节点。


然后,将下一个字段设置为volatile将正确同步。重要的是要理解为什么。你有三组写入之前,一个到节点对象,一个到字段和一个到下一个节点(虽然不完全确定为什么你这样做,也许我错过理解的东西)。



这是2 +(N字段)写入。在这一点上没有发生先前的关系,并且如果节点被正常写入,则不能保证。一旦你写入易失性字段,所有以前的写入都将可见。



2。



在x86(或任何缓存一致性)操作系统上的易失性读/写具有以下属性:


  volatile-read:非常接近正常读取
volatile-write:约为同步写入的时间的1/3 1/3 $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $


3。



您将必须创建VolatileNode和Node。有一个提议要求Java 7提供一个 Fences API,您可以指定要使用静态实用程序类执行的读/写类型,但不会像发布





Thkala做了一个很棒的点,我觉得值得包括


要指出的是
之前的JSR133 JVM(即Java <5.0)没有
不具有相同的语义


所以我写的不适用于在Java 1.4或更低版本中运行的应用程序。


I've got a data structure which consists of linked nodes. You can think of it as of a simple LinkedList. Each node of the list consists of some value and a next field pointing the other node or null if it is the last node. The first node works as a root, it has no value it only points to the next node. All the other nodes are practically immutable that is once they are created neither their value nor their next field change during lifetime, unless the structure is being disposed which relates to a specific situation.

One (only one) thread adds new nodes to the front of the list. It is accomplished by constructing a new object, setting its fields and setting the next field to the object pointed by the root, then setting the root's next field to this new node.

The other nodes browse through the structure only performing reads. They have a reference to the root node, then they go through the other nodes until they find what are looking for or reach the end of the list.

My question is: is it sufficient to make the next field volatile? From my understanding of java memory model, if the main thread (the one that adds new nodes) will perform a volatile write when adding a new node then everything will be synchronized just fine and no inconsistencies will occur.

Also is it right to assume that on x86 architecture reads of a volatile variable won't incur any performance degradation? As the other threads will frequently browse through the structure reading the next field it is important that this can be done freely without any memory barriers etc.

I also have one more concern. The threads that are going to browse the structure are also going to hold some additional nodes. These nodes will be completely thread-local that is they are going to be used by only the thread that created them and are not going to be shared at all. For these additional nodes it is unnecessary for the next field to be volatile. Moreover setting the volatile next field will issue a memory barrier which will cause an undesirable performance loss. I wonder is there a way to avoid this. Ideally it would be just perfect if the next field would work sometimes as a volatile field and sometimes as a normal field ;) or if I had a full control and could issue memory barriers on my own, whenever I need.

Edit:

I also wondered would it be possible to somehow synchronize all these writes on a different volatile variable? For example some other completely unrelated static variable? Since volatile write flushes all the pending writes, wouldn't it be possible for the next field not to be volatile and instead a different volatile variable would be written after the updating thread does all the work?

It does not look very safe to me since there is no happens before relation and the previous writes might get reordered. Next field assignments could be reoredered with the value fields assignments leading to iterating threads observing inconsistent object state.

But maybe it is possible to come up with such a scheme that would be safe? How about this one:

updating thread first constructs a new object, initializes its value fields, sets its next field to node pointed by the root node, performs a volatile write on some static variable, sets the next field of the root node to the newly created node

解决方案

1.

Based on what you say here

constructing a new object, setting its fields and setting the next field to the object pointed by the root, then setting the root's next field to this new node.

Then yes, setting the next field to volatile will correctly synchronize. Its important to understand why. You have three sets of writes before hand, the one to the node object, one to the fields and one to the nodes next (though not completely sure why you are doing that, maybe I miss understand something).

So that's 2 + (N number of field) writes. At this point there is no happens-before relationship and if the node is written normally there is no guarantee. As soon as you write to the volatile field all previous writes will now also be visible.

2.

Volatile reads/writes on a x86 (or any cache-coherent) operating system has the following attributes:

 volatile-read: very close to a normal read
 volatile-write: about 1/3 the time of a synchronization write 
         (whether within intrinsic locking or  j.u.c.Lock locking)

3.

Looks like you will have to create VolatileNode and Node. There was a proposal for Java 7 to come out with a Fences API which you can specify which style of reading/write you want to execute with a static utility class but doesn't look like its releasing

Edit:

Thkala made a great point I feel is worth including

although it should be pointed out that pre-JSR133 JVMs (i.e. Java < 5.0) did not have the same semantics

So what I wrote does not apply to applications run in Java 1.4 or less.

这篇关于易失性变量和内存屏障的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆