java中的易失变量和内存障碍
我有一个由链接节点组成的数据结构。您可以将其视为一个简单的LinkedList。列表的每个节点都由一些值和指向另一个节点的下一个字段组成,如果是最后一个节点,则为null。第一个节点作为根节点工作,它没有值,只指向下一个节点。所有其他节点实际上都是不可变的,即一旦它们被创建,它们的值和它们的下一个字段在生存期内都不会改变,除非正在处理与特定情况相关的结构 一个(只有一个)线程将新节点添加到列表的前面。它是通过构造一个新对象、设置其字段并将下一个字段设置为根指向的对象,然后将根的下一个字段设置为这个新节点来完成的 其他节点浏览结构,仅执行读取。它们引用根节点,然后遍历其他节点,直到找到要查找的内容或到达列表的末尾 我的问题是:这足以让下一个字段不稳定吗?根据我对java内存模型的理解,如果主线程(添加新节点的线程)在添加新节点时执行易失性写操作,那么所有内容都会很好地同步,不会出现不一致的情况 假设在x86体系结构上读取易失性变量不会导致任何性能下降,这是否正确?由于其他线程将经常浏览结构,读取下一个字段,因此这可以在没有任何内存障碍的情况下自由完成,这一点很重要 我还有一个担忧。要浏览结构的线程还将包含一些额外的节点。这些节点将完全是线程本地的,也就是说,它们将仅由创建它们的线程使用,而不会被共享。 对于这些额外的节点,下一个字段不必是易失性的。此外,设置volatile next字段将发出内存屏障,这将导致不希望的性能损失。 我想知道有没有办法避免这种情况。理想情况下,如果下一个字段有时作为易失性字段工作,有时作为正常字段工作,这将是完美的;)或者,如果我有完全的控制权,可以在任何需要的时候自行设置记忆障碍 编辑: 我还想知道是否有可能以某种方式在不同的易失性变量上同步所有这些写操作?例如,其他完全不相关的静态变量?由于volatile write会刷新所有挂起的写操作,那么下一个字段不可能是volatile的,而是在更新线程完成所有工作后会写入另一个volatile变量吗 对我来说,它看起来不太安全,因为没有before关系,以前的写入可能会被重新排序。下一个字段分配可以使用值字段分配重新排序,从而导致迭代线程观察到不一致的对象状态 但也许有可能想出这样一个安全的方案?这个怎么样:java中的易失变量和内存障碍,java,multithreading,concurrency,volatile,memory-barriers,Java,Multithreading,Concurrency,Volatile,Memory Barriers,我有一个由链接节点组成的数据结构。您可以将其视为一个简单的LinkedList。列表的每个节点都由一些值和指向另一个节点的下一个字段组成,如果是最后一个节点,则为null。第一个节点作为根节点工作,它没有值,只指向下一个节点。所有其他节点实际上都是不可变的,即一旦它们被创建,它们的值和它们的下一个字段在生存期内都不会改变,除非正在处理与特定情况相关的结构 一个(只有一个)线程将新节点添加到列表的前面。它是通过构造一个新对象、设置其字段并将下一个字段设置为根指向的对象,然后将根的下一个字段设置为这
更新线程首先构造一个新对象,初始化其值字段,将其下一个字段设置为根节点指向的节点,对某个静态变量执行易失性写入,将根节点的下一个字段设置为新创建的节点使
下一个
字段不稳定会对节点类的所有实例施加内存障碍,而不仅仅是根节点。我认为这比在根节点上使用synchronized
要昂贵。此外,JVM可以更好地优化synchronized
方法调用。另见和
也就是说,您可能应该同时尝试benchmark/profile和benchmark/profile,看看会发生什么
根据你在这里说的
构造新对象,设置其字段和
将下一个字段设置为对象
由根指向,然后设置
此新节点的根的下一个字段
然后是,将下一个字段设置为volatile将正确同步。了解原因很重要。您手头有三组写入操作,一组写入到节点对象,一组写入到字段,另一组写入到下一个节点(虽然不完全确定您为什么要这样做,可能我不理解某些内容)
这就是2+(N个字段)写入。此时,不存在before关系,如果节点正常写入,则无法保证。一旦您写入volatile字段,所有以前的写入现在也将可见
二,
x86(或任何缓存一致)操作系统上的易失性读/写具有以下属性:
三,
看起来您必须创建挥发节点和节点。有人建议Java7提供一个API,您可以指定要使用静态实用程序类执行的读/写风格,但看起来不像它的发布
编辑:
特卡拉提出了一个很好的观点,我认为值得包括在内
尽管应该指出
JSR133之前的JVM(即Java<5.0)没有
没有相同的语义
因此,我写的内容不适用于在Java 1.4或更低版本中运行的应用程序。您的根节点实际上不需要是节点。您只需要引用第一个“真实”节点 因此,您不需要在所有节点中设置
next
字段;这些节点根本不同步。
如果您不介意对第一个节点的易失性访问的成本,您可以将该类用于非同步链接列表。或者,您可以简单地用非易失性的
firstNode
重写非同步版本的类。我并不完全相信这一点。如果“将对节点类的所有实例施加内存障碍”为true,则ConcurrentHashMap将引发sam
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)
public class LinkedList {
private volatile Node firstNode;
...
addNode(Node node) {
node.next = firstNode;
firstNode = node;
}
}