为了更好地理解JLS中的文本,我也在阅读Jeremy Manson(JMM的创建者之一)的Java内存模型。
该论文包含了让我感兴趣的示例:如果一个具有final字段的对象o在构造函数完成之前不正确地向另一个线程t公开两次:
首先,在o的构造函数完成之前“不正确地”;
接下来,在o的构造函数完成之后“正确地”。
然后,即使只通过“正确地”发布的路径访问,t也可以看到半构造的o。
以下是论文的一部分:
我尝试在当前的JLS中找到任何明确允许或禁止这种行为的内容,但我发现:Figure 7.3: Example of Simple Final Semantics
f1 is a final field; its default value is 0
Thread 1 Thread 2 Thread 3
o.f1 = 42; p = o; freeze o.f1; q = o;
r1 = p; i = r1.f1; r2 = q; if (r2 == r1) k = r2.f1;
r3 = q; j = r3.f1;
We assume r1, r2 and r3 do not see the value null. i and k can be 0 or 42, and j must be 42.
Consider Figure 7.3. We will not start out with the complications of multiple writes to final fields; a freeze, for the moment, is simply what happens at the end of a constructor. Although
r1
,r2
andr3
can see the valuenull
, we will not concern ourselves with that; that just leads to a null pointer exception....
What about the read of
q.f1
in Thread 2? Is that guaranteed to see the correct value for the final field? A compiler could determine thatp
andq
point to the same object, and therefore reuse the same value for bothp.f1
andq.f1
for that thread. We want to allow the compiler to remove redundant reads of final fields wherever possible, so we allowk
to see the value 0.One way to conceptualize this is by thinking of an object being “tainted’ for a thread if that thread reads an incorrectly published reference to the object. If an object is tainted for a thread, the thread is never guaranteed to see the object’s correctly constructed final fields. More generally, if a thread
t
reads an incorrectly published reference to an objecto
, threadt
forever sees a tainted version ofo
without any guarantees of seeing the correct value for the final fields ofo
.
当一个对象的构造函数完成时,该对象被认为是完全初始化的。只能在该对象完全初始化之后才能看到对该对象的引用的线程保证能够看到该对象的最终字段的正确初始化值。
当前的JLS是否允许这样的行为?
i = r.f1;
应该真的是i = r1.f1;
而不是i = r.f1;
,if (r2 == r)
应该是if (r2 == r1)
。还有freeze o.f
-f
是什么?应该是f1
吧?我还假设他所说的 "freeze" 意味着适当的内存屏障?然后:"Thread 2 中对q.f1
的读取怎么样?" 它不是q.f1
,而是r2.f1
。 - Eugeneq.f1
是没有问题的:它意味着我们通过共享变量q
访问o.f1
(就像在 JLS 中的r1,r2,r3
是局部变量,i,j,k,p,q
是共享变量一样)。 - user15094989q.f1
不好,但你实际上在哪里看到它的呢?真正的读取应该是r2.f1
。 - Eugene