如果 a不安全地发布,理论上另一个线程在读取两次Foo
时可以观察到两个不同的值。n
由于这个原因,以下程序可能会失败。
public static Foo shared;
public static void main(String[] args)
{
new Thread(){
@Override
public void run()
{
while(true)
{
Foo foo = shared;
if(foo!=null)
foo.check();
}
}
}.start();
new Thread(){
@Override
public void run()
{
while(true)
{
shared = new Foo(1); // unsafe publication
}
}
}.start();
}
然而,几乎不可能观察到它失败了。VM 可能会优化n!=n
到false
没有实际读取n
两次。
但是我们可以展示一个等价的程序,即就 Java 内存模型而言,对先前程序的有效转换,并观察它立即失败
static public class Foo
{
int n;
public Foo()
{
}
public void check()
{
int n1 = n;
no_op();
int n2 = n;
if (n1 != n2)
throw new AssertionError("huh?");
}
}
// calling this method has no effect on memory semantics
static void no_op()
{
if(Math.sin(1)>1) System.out.println("never");
}
public static Foo shared;
public static void main(String[] args)
{
new Thread(){
@Override
public void run()
{
while(true)
{
Foo foo = shared;
if(foo!=null)
foo.check();
}
}
}.start();
new Thread(){
@Override
public void run()
{
while(true)
{
// a valid transformation of `shared=new Foo(1)`
Foo foo = new Foo();
shared = foo;
no_op();
foo.n = 1;
}
}
}.start();
}