
在当前的Java内存模型领域中,
volatile不等于
final。换句话说,您不能将替换
final为
volatile,并认为安全构造保证是相同的。值得注意的是,这在理论上可能会发生:
public class M { volatile int x; M(int v) { this.x = v; } int x() { return x; }}// thread 1m = new M(42);// thread 2M lm;while ((lm = m) == null); // wait for itprint(lm.x()); // allowed to print "0"因此,
volatile在构造函数中编写字段并不安全。
直觉:
m上面的例子中有一场竞赛。创造 场地 并不能消除种族
M.x
volatile,只有制造
m自己
volatile会有所帮助。换句话说,
volatile该示例中的修饰符在 错误的位置
可用。在安全发布中,您必须具有“写入->易失性写入->易失性读取,可观察到易失性写入->读取(现在先观察易失性写入之前的写入)”,而必须具有“易失性写入->写入->读取-
琐事1:
此属性表示我们可以
volatile在构造函数中更加积极地进行优化。这证实了
this可以放松未观察到的易失性存储的直觉(实际上,直到具有非转义完成的构造函数才观察到)。
琐事2:
这也意味着您不能安全地初始化
volatile变量。在上面的示例中替换
M为
AtomicInteger,您将具有独特的现实生活行为!调用
newAtomicInteger(42)一个线程,不安全地发布该实例,然后
get()在另一个线程中执行-
您是否保证遵守
42?如前所述,JMM说“不”。Java内存模型的最新修订版努力确保所有初始化的安全构造,以捕获这种情况。许多重要的非x86端口已经加强了它的安全性。
Trivia 3: Doug Lea:“
finalvs
volatile问题导致在java.util.concurrent中进行了一些曲折的构造,以允许在自然不存在的情况下将0用作基本/默认值。该规则很糟糕,应该更改。”
也就是说,可以使示例更加狡猾:
public class C { int v; C(int v) { this.x = v; } int x() { return x; } }public class M { volatile C c; M(int v) { this.c = new C(v); } int x() { while (c == null); // wait! return c.x(); }}// thread 1m = new M(42);// thread 2M lm;while ((lm = m) == null); // wait for itprint(lm.x()); // always prints "42"如果 在 volatile读取观察到volatile写入构造函数中写入的值 之后
存在一个传递性直读
volatile字段,则通常的安全发布规则开始生效。 __
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)