【问题标题】:java - volatile keyword also for non-primitivesjava - volatile 关键字也适用于非基元
【发布时间】:2012-05-15 20:16:23
【问题描述】:

我不确定 volatile 关键字是否也应该用于非基元。我有一个由一个线程设置/分配并由另一个线程访问的类成员。我应该声明这个成员 volatile 吗?

private /* volatile */ Object o;

public void setMember(Object o) {
    this.o = o;
}

public Object getMember() {
    return o;
}

这里,setMember(...) 由一个线程调用,getMember() 由另一个线程调用。

如果它是一个布尔值,例如,答案是肯定的。

我使用的是 Java 1.4,并且这种情况下的成员是只读的。所以我只关心在这种情况下的可见性,因此我对 volatile 关键字的问题。

【问题讨论】:

    标签: java volatile


    【解决方案1】:

    是的 - volatile 对引用类型字段的意义与它对原始类型字段的意义完全相同。除了在引用类型的情况下,字段所引用的对象的成员必须为多线程访问而设计。

    【讨论】:

    • 只是对这句话感到好奇——该字段所指的对象的成员也必须为多线程访问而设计——如果你能详细说明为什么会这样,那就太好了?
    • @jtkSource: volatile 仅影响字段,但字段仅包含引用。因此 volatile 确保其他线程看到对该引用的更新,但如果被引用的对象没有正确使用 volatile 或同步,那么其他线程可能无法看到该对象字段的更新。
    • 只是想一想 - 所以如果一个 volatile 变量引用一个对象,在声明它为 volatile 的对象的上下文中,非易失性变量是否也会以一致的方式“可见” ?如果还有其他对象也以非易失性方式引用此“引用对象”,那么是否只有这些对象有看不到处于一致状态的变量的风险?内存中是否有可能存在同一对象的两种表示形式? - 如果我混淆了我的逻辑,请告诉我。
    • @jtkSource 否、否和是(这两种表示形式将存在于不同 CPU 内核的缓存中)。 volatile only 对一个变量有效果,这将是引用本身。它对参考点所在的对象没有任何影响。
    【解决方案2】:

    您可以,而且可能会有所帮助,但请记住该关键字仅适用于参考设置。它对该对象内部属性的多线程可见性没有影响。如果它是有状态的,您可能希望围绕对它的每次访问进行同步,以确保所需的先发生关系。

    【讨论】:

      【解决方案3】:

      是的,您的代码是正确的。在这种情况下,引用本身是可变的,因此引用的机会在所有其他线程中自动可见,但不会更改被引用的对象。

      【讨论】:

        【解决方案4】:

        如果我们查找 AtomicInteger 类,它已将 value 声明为 volatile ,因此它可以在多线程环境中使用而不会出现任何线程缓存问题。

        public class AtomicInteger {
            private volatile int value;
        
            /**
             * Gets the current value.
             *
             * @return the current value
             */
            public final int get() {
                return value;
            }
        
            /**
             * Sets to the given value.
             *
             * @param newValue the new value
             */
            public final void set(int newValue) {
                value = newValue;
            }
        
        }
        

        但是,如果您认为对 AtomicInteger 的引用,它将被许多线程使用不同的 AtomicInteger 对象修改;那么你也需要 volatile 作为该参考。

        private volatile AtomicInteger reference = new AtomicInteger(0);
        

        大多数情况下并非如此;只有对象的值会改变;因此将其声明为 final。

        private final AtomicInteger reference = new AtomicInteger(0);
        

        【讨论】: