我发现了这句话:
在正确构造的物体中,全部 线程会看到正确的值 最终的领域,无论如何 对象发布。
那么为什么要使用volatile变量来安全 发布一个不可变对象?
我真的很困惑。任何人都能用合适的例子说清楚吗?
答案 0 :(得分:5)
在这种情况下,波动率只会确保新对象的可见性;碰巧通过非易失性字段获取对象的任何其他线程确实会根据JSR-133's initialization safety guarantees看到最终字段的正确值。
尽管如此,使变量volatile不会受到伤害;从内存管理的角度看是正确的;对于在构造函数中初始化的非final字段,将是必需的(尽管在不可变对象中不应存在任何这些字段)。如果您希望在线程之间共享变量,则需要确保足够的同步以提供可见性;虽然在这种情况下你是对的,但构造函数的 atomicity 没有危险。
感谢Tom Hawtin指出我完全忽视了JMM对最终领域的保证;之前的错误答案如下。
volatile变量的原因是在对象的构造和赋值之间建立了 happens-before 关系(根据Java内存模型)变量。这实现了两件事:
由于不可变对象的基本规则之一是您在构造函数期间不发布引用,因此这是第二点,可能在此引用。在没有适当并发处理的多线程环境中, 可以在构造该对象之前对要“发布”的对象进行引用。因此,另一个线程可以获取该对象,看到它的一个字段是null
,然后看到这个“不可变”对象已经改变。
请注意,如果您有其他适当的同步原语,则不必使用volatile字段来实现此目的 - 例如,如果赋值(以及所有后续读取)都在{{{ 1}}在给定的监视器上阻塞 - 但是在“独立”意义上,将变量标记为 synchronized
是告诉JVM的最简单方法“这可能被多个线程读取,请使分配安全上下文中,“
答案 1 :(得分:2)
对不可变对象的volatile引用可能很有用。这将允许您将一个对象替换为另一个对象,以使新数据可用于其他线程。
我建议您首先使用AtomicReference。
如果您需要最终的易失性字段,则会出现问题。一旦构造函数返回,所有字段(包括最终字段)都可用于其他线程。因此,如果将对象传递给构造函数中的另一个线程,则另一个线程可能会看到不一致的状态。恕我直言,你应该考虑一个不同的解决方案,所以你不必这样做。
答案 2 :(得分:0)
您无法真正看到Immutable类的差异。请参阅下面的示例。Myclass.class
public static Foo getInstance(){
if(INSTANCE == null){
INSTANCE = new Foo();
}
return INSTANCE;
}
在上面的代码中,如果Foo被声明为 final (final Foo INSTANCE;
),它保证在构造函数调用期间它不会发布引用。部分对象构造是不可能的
考虑这个......如果这个Myclass
是不可变的,它的状态在对象构造之后就不会改变,使得Volatile(volatile final Foo INSTANCE;
)关键字变得冗余。但是如果这个类允许它的对象状态是更改(不可变)多个线程CAN实际更新对象,并且某些更新对其他线程不可见,因此volatile关键字确保在非Immutable类中安全发布对象。